File manager - Edit - /home/jardides/www/Jardi-design/images/administrator/lib.tar
Back
ThemingDefaults.php 0000604 00000021131 15247100611 0010327 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Theming; use OCP\Files\IAppData; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; class ThemingDefaults extends \OC_Defaults { /** @var IConfig */ private $config; /** @var IL10N */ private $l; /** @var IURLGenerator */ private $urlGenerator; /** @var IAppData */ private $appData; /** @var ICacheFactory */ private $cacheFactory; /** @var string */ private $name; /** @var string */ private $url; /** @var string */ private $slogan; /** @var string */ private $color; /** @var Util */ private $util; /** @var string */ private $iTunesAppId; /** @var string */ private $iOSClientUrl; /** @var string */ private $AndroidClientUrl; /** * ThemingDefaults constructor. * * @param IConfig $config * @param IL10N $l * @param IURLGenerator $urlGenerator * @param \OC_Defaults $defaults * @param IAppData $appData * @param ICacheFactory $cacheFactory * @param Util $util */ public function __construct(IConfig $config, IL10N $l, IURLGenerator $urlGenerator, IAppData $appData, ICacheFactory $cacheFactory, Util $util ) { parent::__construct(); $this->config = $config; $this->l = $l; $this->urlGenerator = $urlGenerator; $this->appData = $appData; $this->cacheFactory = $cacheFactory; $this->util = $util; $this->name = parent::getName(); $this->url = parent::getBaseUrl(); $this->slogan = parent::getSlogan(); $this->color = parent::getColorPrimary(); $this->iTunesAppId = parent::getiTunesAppId(); $this->iOSClientUrl = parent::getiOSClientUrl(); $this->AndroidClientUrl = parent::getAndroidClientUrl(); } public function getName() { return strip_tags($this->config->getAppValue('theming', 'name', $this->name)); } public function getHTMLName() { return $this->config->getAppValue('theming', 'name', $this->name); } public function getTitle() { return $this->getName(); } public function getEntity() { return $this->getName(); } public function getBaseUrl() { return $this->config->getAppValue('theming', 'url', $this->url); } public function getSlogan() { return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', $this->slogan)); } public function getShortFooter() { $slogan = $this->getSlogan(); $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' . ' rel="noreferrer">' .$this->getEntity() . '</a>'. ($slogan !== '' ? ' – ' . $slogan : ''); return $footer; } /** * Color that is used for the header as well as for mail headers * * @return string */ public function getColorPrimary() { return $this->config->getAppValue('theming', 'color', $this->color); } /** * Themed logo url * * @param bool $useSvg Whether to point to the SVG image or a fallback * @return string */ public function getLogo($useSvg = true) { $logo = $this->config->getAppValue('theming', 'logoMime', false); $logoExists = true; try { $this->appData->getFolder('images')->getFile('logo'); } catch (\Exception $e) { $logoExists = false; } $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); if(!$logo || !$logoExists) { if($useSvg) { $logo = $this->urlGenerator->imagePath('core', 'logo.svg'); } else { $logo = $this->urlGenerator->imagePath('core', 'logo.png'); } return $logo . '?v=' . $cacheBusterCounter; } return $this->urlGenerator->linkToRoute('theming.Theming.getLogo') . '?v=' . $cacheBusterCounter; } /** * Themed background image url * * @return string */ public function getBackground() { $backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime',false); $backgroundExists = true; try { $this->appData->getFolder('images')->getFile('background'); } catch (\Exception $e) { $backgroundExists = false; } $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); if(!$backgroundLogo || !$backgroundExists) { return $this->urlGenerator->imagePath('core','background.jpg') . '?v=' . $cacheBusterCounter; } return $this->urlGenerator->linkToRoute('theming.Theming.getLoginBackground') . '?v=' . $cacheBusterCounter; } /** * @return string */ public function getiTunesAppId() { return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId); } /** * @return string */ public function getiOSClientUrl() { return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl); } /** * @return string */ public function getAndroidClientUrl() { return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl); } /** * @return array scss variables to overwrite */ public function getScssVariables() { $cache = $this->cacheFactory->create('theming'); if ($value = $cache->get('getScssVariables')) { return $value; } $variables = [ 'theming-cachebuster' => "'" . $this->config->getAppValue('theming', 'cachebuster', '0') . "'", 'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime', '') . "'", 'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime', '') . "'" ]; $variables['image-logo'] = "'".$this->urlGenerator->getAbsoluteURL($this->getLogo())."'"; $variables['image-login-background'] = "'".$this->urlGenerator->getAbsoluteURL($this->getBackground())."'"; $variables['image-login-plain'] = 'false'; if ($this->config->getAppValue('theming', 'color', null) !== null) { if ($this->util->invertTextColor($this->getColorPrimary())) { $colorPrimaryText = '#000000'; } else { $colorPrimaryText = '#ffffff'; } $variables['color-primary'] = $this->getColorPrimary(); $variables['color-primary-text'] = $colorPrimaryText; $variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary()); } if ($this->config->getAppValue('theming', 'backgroundMime', null) === 'backgroundColor') { $variables['image-login-plain'] = 'true'; } $cache->set('getScssVariables', $variables); return $variables; } /** * Check if Imagemagick is enabled and if SVG is supported * otherwise we can't render custom icons * * @return bool */ public function shouldReplaceIcons() { $cache = $this->cacheFactory->create('theming'); if($value = $cache->get('shouldReplaceIcons')) { return (bool)$value; } $value = false; if(extension_loaded('imagick')) { $checkImagick = new \Imagick(); if (count($checkImagick->queryFormats('SVG')) >= 1) { $value = true; } $checkImagick->clear(); } $cache->set('shouldReplaceIcons', $value); return $value; } /** * Increases the cache buster key */ private function increaseCacheBuster() { $cacheBusterKey = $this->config->getAppValue('theming', 'cachebuster', '0'); $this->config->setAppValue('theming', 'cachebuster', (int)$cacheBusterKey+1); $this->cacheFactory->create('theming')->clear('getScssVariables'); } /** * Update setting in the database * * @param string $setting * @param string $value */ public function set($setting, $value) { $this->config->setAppValue('theming', $setting, $value); $this->increaseCacheBuster(); } /** * Revert settings to the default value * * @param string $setting setting which should be reverted * @return string default value */ public function undo($setting) { $this->config->deleteAppValue('theming', $setting); $this->increaseCacheBuster(); switch ($setting) { case 'name': $returnValue = $this->getEntity(); break; case 'url': $returnValue = $this->getBaseUrl(); break; case 'slogan': $returnValue = $this->getSlogan(); break; case 'color': $returnValue = $this->getColorPrimary(); break; default: $returnValue = ''; break; } return $returnValue; } } Settings/Section.php 0000604 00000003660 15247100611 0010457 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\LogReader\Settings; use OCP\IL10N; use OCP\IURLGenerator; use OCP\Settings\IIconSection; class Section implements IIconSection { /** @var IL10N */ private $l; /** @var IURLGenerator */ private $url; public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * returns the ID of the section. It is supposed to be a lower case string, * e.g. 'ldap' * * @returns string */ public function getID() { return 'logging'; } /** * returns the translated name as it should be displayed, e.g. 'LDAP / AD * integration'. Use the L10N service to translate it. * * @return string */ public function getName() { return $this->l->t('Logging'); } /** * @return int whether the form should be rather on the top or bottom of * the settings navigation. The sections are arranged in ascending order of * the priority values. It is required to return a value between 0 and 99. * * E.g.: 70 */ public function getPriority() { return 90; } /** * {@inheritdoc} */ public function getIcon() { return $this->url->imagePath('logreader', 'app-dark.svg'); } } Settings/Admin.php 0000604 00000003030 15247100611 0010072 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\LogReader\Settings; use OCP\AppFramework\Http\TemplateResponse; use OCP\Settings\ISettings; class Admin implements ISettings { /** * @return TemplateResponse */ public function getForm() { return new TemplateResponse('logreader', 'index', ['appId' => 'logreader', 'inline-settings' => 'true'], ''); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection() { return 'logging'; } /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 */ public function getPriority() { return 90; } } Controller/IconController.php 0000604 00000014140 15247100611 0012325 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Julius Haertl <jus@bitgrid.net> * * @author Julius Haertl <jus@bitgrid.net> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Theming\Controller; use OC\IntegrityCheck\Helpers\FileAccessHelper; use OCA\Theming\IconBuilder; use OCA\Theming\ImageManager; use OCA\Theming\ThemingDefaults; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\NotFoundException; use OCP\IRequest; use OCA\Theming\Util; use OCP\IConfig; class IconController extends Controller { /** @var ThemingDefaults */ private $themingDefaults; /** @var Util */ private $util; /** @var ITimeFactory */ private $timeFactory; /** @var IConfig */ private $config; /** @var IconBuilder */ private $iconBuilder; /** @var ImageManager */ private $imageManager; /** @var FileAccessHelper */ private $fileAccessHelper; /** * IconController constructor. * * @param string $appName * @param IRequest $request * @param ThemingDefaults $themingDefaults * @param Util $util * @param ITimeFactory $timeFactory * @param IConfig $config * @param IconBuilder $iconBuilder * @param ImageManager $imageManager */ public function __construct( $appName, IRequest $request, ThemingDefaults $themingDefaults, Util $util, ITimeFactory $timeFactory, IConfig $config, IconBuilder $iconBuilder, ImageManager $imageManager, FileAccessHelper $fileAccessHelper ) { parent::__construct($appName, $request); $this->themingDefaults = $themingDefaults; $this->util = $util; $this->timeFactory = $timeFactory; $this->config = $config; $this->iconBuilder = $iconBuilder; $this->imageManager = $imageManager; $this->fileAccessHelper = $fileAccessHelper; } /** * @PublicPage * @NoCSRFRequired * * @param $app string app name * @param $image string image file name (svg required) * @return FileDisplayResponse|NotFoundResponse */ public function getThemedIcon($app, $image) { try { $iconFile = $this->imageManager->getCachedImage("icon-" . $app . '-' . str_replace("/","_",$image)); } catch (NotFoundException $exception) { $icon = $this->iconBuilder->colorSvg($app, $image); if ($icon === false || $icon === "") { return new NotFoundResponse(); } $iconFile = $this->imageManager->setCachedImage("icon-" . $app . '-' . str_replace("/","_",$image), $icon); } if ($iconFile !== false) { $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']); $response->cacheFor(86400); $expires = new \DateTime(); $expires->setTimestamp($this->timeFactory->getTime()); $expires->add(new \DateInterval('PT24H')); $response->addHeader('Expires', $expires->format(\DateTime::RFC2822)); $response->addHeader('Pragma', 'cache'); return $response; } else { return new NotFoundResponse(); } } /** * Return a 32x32 favicon as png * * @PublicPage * @NoCSRFRequired * * @param $app string app name * @return FileDisplayResponse|DataDisplayResponse */ public function getFavicon($app = "core") { $response = null; if ($this->themingDefaults->shouldReplaceIcons()) { try { $iconFile = $this->imageManager->getCachedImage('favIcon-' . $app); } catch (NotFoundException $exception) { $icon = $this->iconBuilder->getFavicon($app); $iconFile = $this->imageManager->setCachedImage('favIcon-' . $app, $icon); } if ($iconFile !== false) { $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); } } if($response === null) { $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon.png'; $response = new DataDisplayResponse($this->fileAccessHelper->file_get_contents($fallbackLogo), Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); } $response->cacheFor(86400); $expires = new \DateTime(); $expires->setTimestamp($this->timeFactory->getTime()); $expires->add(new \DateInterval('PT24H')); $response->addHeader('Expires', $expires->format(\DateTime::RFC2822)); $response->addHeader('Pragma', 'cache'); return $response; } /** * Return a 512x512 icon for touch devices * * @PublicPage * @NoCSRFRequired * * @param $app string app name * @return FileDisplayResponse|NotFoundResponse */ public function getTouchIcon($app = "core") { $response = null; if ($this->themingDefaults->shouldReplaceIcons()) { try { $iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app); } catch (NotFoundException $exception) { $icon = $this->iconBuilder->getTouchIcon($app); $iconFile = $this->imageManager->setCachedImage('touchIcon-' . $app, $icon); } if ($iconFile !== false) { $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/png']); } } if($response === null) { $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon-touch.png'; $response = new DataDisplayResponse($this->fileAccessHelper->file_get_contents($fallbackLogo), Http::STATUS_OK, ['Content-Type' => 'image/png']); } $response->cacheFor(86400); $expires = new \DateTime(); $expires->setTimestamp($this->timeFactory->getTime()); $expires->add(new \DateInterval('PT24H')); $response->addHeader('Expires', $expires->format(\DateTime::RFC2822)); $response->addHeader('Pragma', 'cache'); return $response; } } Controller/ThemingController.php 0000604 00000027636 15247100611 0013046 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Julius Haertl <jus@bitgrid.net> * @author Lukas Reschke <lukas@statuscode.ch> * @author oparoz <owncloud@interfasys.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Theming\Controller; use OC\Files\AppData\Factory; use OC\Template\SCSSCacher; use OCA\Theming\ThemingDefaults; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataDownloadResponse; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\File; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; use OCA\Theming\Util; use OCP\ITempManager; use OCP\IURLGenerator; /** * Class ThemingController * * handle ajax requests to update the theme * * @package OCA\Theming\Controller */ class ThemingController extends Controller { /** @var ThemingDefaults */ private $themingDefaults; /** @var Util */ private $util; /** @var ITimeFactory */ private $timeFactory; /** @var IL10N */ private $l10n; /** @var IConfig */ private $config; /** @var ITempManager */ private $tempManager; /** @var IAppData */ private $appData; /** @var SCSSCacher */ private $scssCacher; /** @var IURLGenerator */ private $urlGenerator; /** * ThemingController constructor. * * @param string $appName * @param IRequest $request * @param IConfig $config * @param ThemingDefaults $themingDefaults * @param Util $util * @param ITimeFactory $timeFactory * @param IL10N $l * @param ITempManager $tempManager * @param IAppData $appData * @param SCSSCacher $scssCacher * @param IURLGenerator $urlGenerator */ public function __construct( $appName, IRequest $request, IConfig $config, ThemingDefaults $themingDefaults, Util $util, ITimeFactory $timeFactory, IL10N $l, ITempManager $tempManager, IAppData $appData, SCSSCacher $scssCacher, IURLGenerator $urlGenerator ) { parent::__construct($appName, $request); $this->themingDefaults = $themingDefaults; $this->util = $util; $this->timeFactory = $timeFactory; $this->l10n = $l; $this->config = $config; $this->tempManager = $tempManager; $this->appData = $appData; $this->scssCacher = $scssCacher; $this->urlGenerator = $urlGenerator; } /** * @param string $setting * @param string $value * @return DataResponse * @internal param string $color */ public function updateStylesheet($setting, $value) { $value = trim($value); switch ($setting) { case 'name': if (strlen($value) > 250) { return new DataResponse([ 'data' => [ 'message' => $this->l10n->t('The given name is too long'), ], 'status' => 'error' ]); } break; case 'url': if (strlen($value) > 500) { return new DataResponse([ 'data' => [ 'message' => $this->l10n->t('The given web address is too long'), ], 'status' => 'error' ]); } break; case 'slogan': if (strlen($value) > 500) { return new DataResponse([ 'data' => [ 'message' => $this->l10n->t('The given slogan is too long'), ], 'status' => 'error' ]); } break; case 'color': if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) { return new DataResponse([ 'data' => [ 'message' => $this->l10n->t('The given color is invalid'), ], 'status' => 'error' ]); } break; } $this->themingDefaults->set($setting, $value); // reprocess server scss for preview $cssCached = $this->scssCacher->process(\OC::$SERVERROOT, '/core/css/server.scss', 'core'); return new DataResponse( [ 'data' => [ 'message' => $this->l10n->t('Saved'), 'serverCssUrl' => $this->urlGenerator->linkTo('', $this->scssCacher->getCachedSCSS('core', '/core/css/server.scss')) ], 'status' => 'success' ] ); } /** * Update the logos and background image * * @return DataResponse */ public function updateLogo() { $backgroundColor = $this->request->getParam('backgroundColor', false); if($backgroundColor) { $this->themingDefaults->set('backgroundMime', 'backgroundColor'); return new DataResponse( [ 'data' => [ 'name' => 'backgroundColor', 'message' => $this->l10n->t('Saved') ], 'status' => 'success' ] ); } $newLogo = $this->request->getUploadedFile('uploadlogo'); $newBackgroundLogo = $this->request->getUploadedFile('upload-login-background'); if (empty($newLogo) && empty($newBackgroundLogo)) { return new DataResponse( [ 'data' => [ 'message' => $this->l10n->t('No file uploaded') ] ], Http::STATUS_UNPROCESSABLE_ENTITY ); } $name = ''; try { $folder = $this->appData->getFolder('images'); } catch (NotFoundException $e) { $folder = $this->appData->newFolder('images'); } if (!empty($newLogo)) { $target = $folder->newFile('logo'); $target->putContent(file_get_contents($newLogo['tmp_name'], 'r')); $this->themingDefaults->set('logoMime', $newLogo['type']); $name = $newLogo['name']; } if (!empty($newBackgroundLogo)) { $target = $folder->newFile('background'); $image = @imagecreatefromstring(file_get_contents($newBackgroundLogo['tmp_name'], 'r')); if ($image === false) { return new DataResponse( [ 'data' => [ 'message' => $this->l10n->t('Unsupported image type'), ], 'status' => 'failure', ], Http::STATUS_UNPROCESSABLE_ENTITY ); } // Optimize the image since some people may upload images that will be // either to big or are not progressive rendering. $tmpFile = $this->tempManager->getTemporaryFile(); if (function_exists('imagescale')) { // FIXME: Once PHP 5.5.0 is a requirement the above check can be removed // Workaround for https://bugs.php.net/bug.php?id=65171 $newHeight = imagesy($image) / (imagesx($image) / 1920); $image = imagescale($image, 1920, $newHeight); } imageinterlace($image, 1); imagejpeg($image, $tmpFile, 75); imagedestroy($image); $target->putContent(file_get_contents($tmpFile, 'r')); $this->themingDefaults->set('backgroundMime', $newBackgroundLogo['type']); $name = $newBackgroundLogo['name']; } return new DataResponse( [ 'data' => [ 'name' => $name, 'message' => $this->l10n->t('Saved') ], 'status' => 'success' ] ); } /** * Revert setting to default value * * @param string $setting setting which should be reverted * @return DataResponse */ public function undo($setting) { $value = $this->themingDefaults->undo($setting); // reprocess server scss for preview $cssCached = $this->scssCacher->process(\OC::$SERVERROOT, '/core/css/server.scss', 'core'); if($setting === 'logoMime') { try { $file = $this->appData->getFolder('images')->getFile('logo'); $file->delete(); } catch (NotFoundException $e) { } catch (NotPermittedException $e) { } } if($setting === 'backgroundMime') { try { $file = $this->appData->getFolder('images')->getFile('background'); $file->delete(); } catch (NotFoundException $e) { } catch (NotPermittedException $e) { } } return new DataResponse( [ 'data' => [ 'value' => $value, 'message' => $this->l10n->t('Saved'), 'serverCssUrl' => $this->urlGenerator->linkTo('', $this->scssCacher->getCachedSCSS('core', '/core/css/server.scss')) ], 'status' => 'success' ] ); } /** * @PublicPage * @NoCSRFRequired * * @return FileDisplayResponse|NotFoundResponse */ public function getLogo() { try { /** @var File $file */ $file = $this->appData->getFolder('images')->getFile('logo'); } catch (NotFoundException $e) { return new NotFoundResponse(); } $response = new FileDisplayResponse($file); $response->cacheFor(3600); $expires = new \DateTime(); $expires->setTimestamp($this->timeFactory->getTime()); $expires->add(new \DateInterval('PT24H')); $response->addHeader('Expires', $expires->format(\DateTime::RFC2822)); $response->addHeader('Pragma', 'cache'); $response->addHeader('Content-Type', $this->config->getAppValue($this->appName, 'logoMime', '')); return $response; } /** * @PublicPage * @NoCSRFRequired * * @return FileDisplayResponse|NotFoundResponse */ public function getLoginBackground() { try { /** @var File $file */ $file = $this->appData->getFolder('images')->getFile('background'); } catch (NotFoundException $e) { return new NotFoundResponse(); } $response = new FileDisplayResponse($file); $response->cacheFor(3600); $expires = new \DateTime(); $expires->setTimestamp($this->timeFactory->getTime()); $expires->add(new \DateInterval('PT24H')); $response->addHeader('Expires', $expires->format(\DateTime::RFC2822)); $response->addHeader('Pragma', 'cache'); $response->addHeader('Content-Type', $this->config->getAppValue($this->appName, 'backgroundMime', '')); return $response; } /** * @NoCSRFRequired * @PublicPage * * @return FileDisplayResponse|NotFoundResponse */ public function getStylesheet() { $appPath = substr(\OC::$server->getAppManager()->getAppPath('theming'), strlen(\OC::$SERVERROOT) + 1); /* SCSSCacher is required here * We cannot rely on automatic caching done by \OC_Util::addStyle, * since we need to add the cacheBuster value to the url */ $cssCached = $this->scssCacher->process(\OC::$SERVERROOT, $appPath . '/css/theming.scss', 'theming'); if(!$cssCached) { return new NotFoundResponse(); } try { $cssFile = $this->scssCacher->getCachedCSS('theming', 'theming.css'); $response = new FileDisplayResponse($cssFile, Http::STATUS_OK, ['Content-Type' => 'text/css']); $response->cacheFor(86400); $expires = new \DateTime(); $expires->setTimestamp($this->timeFactory->getTime()); $expires->add(new \DateInterval('PT24H')); $response->addHeader('Expires', $expires->format(\DateTime::RFC1123)); $response->addHeader('Pragma', 'cache'); return $response; } catch (NotFoundException $e) { return new NotFoundResponse(); } } /** * @NoCSRFRequired * @PublicPage * * @return DataDownloadResponse */ public function getJavascript() { $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); $responseJS = '(function() { OCA.Theming = { name: ' . json_encode($this->themingDefaults->getName()) . ', url: ' . json_encode($this->themingDefaults->getBaseUrl()) . ', slogan: ' . json_encode($this->themingDefaults->getSlogan()) . ', color: ' . json_encode($this->themingDefaults->getColorPrimary()) . ', inverted: ' . json_encode($this->util->invertTextColor($this->themingDefaults->getColorPrimary())) . ', cacheBuster: ' . json_encode($cacheBusterValue) . ' }; })();'; $response = new DataDownloadResponse($responseJS, 'javascript', 'text/javascript'); $response->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $response->addHeader('Pragma', 'cache'); $response->cacheFor(3600); return $response; } } Migration/ThemingImages.php 0000604 00000004135 15247100611 0011723 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net> * * @author Julius Härtl <jus@bitgrid.net> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Theming\Migration; use OCA\Theming\ThemingDefaults; use OCP\Files\IAppData; use OCP\Files\IRootFolder; use OCP\Migration\IRepairStep; use OCP\Migration\IOutput; use OC\Files\Node\File; use OCP\Files\NotFoundException; class ThemingImages implements IRepairStep { private $appData; private $rootFolder; public function __construct(IAppData $appData, IRootFolder $rootFolder) { $this->appData = $appData; $this->rootFolder = $rootFolder; } /* * @inheritdoc */ public function getName() { return 'Move theming files to AppData storage'; } /** * @inheritdoc */ public function run(IOutput $output) { $folder = $this->appData->newFolder("images"); /** @var File $file */ $file = null; try { $file = $this->rootFolder->get('themedinstancelogo'); $logo = $folder->newFile('logo'); $logo->putContent($file->getContent()); $file->delete(); } catch (NotFoundException $e) { $output->info('No theming logo image to migrate'); } try { $file = $this->rootFolder->get('themedbackgroundlogo'); $background = $folder->newFile('background'); $background->putContent($file->getContent()); $file->delete(); } catch (NotFoundException $e) { $output->info('No theming background image to migrate'); } } } ImageManager.php 0000604 00000005550 15247100611 0007570 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net> * * @author Julius Härtl <jus@bitgrid.net> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Theming; use OCP\IConfig; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; class ImageManager { /** @var IConfig */ private $config; /** @var IAppData */ private $appData; /** * ImageManager constructor. * * @param IConfig $config * @param IAppData $appData */ public function __construct(IConfig $config, IAppData $appData ) { $this->config = $config; $this->appData = $appData; } /** * Get folder for current theming files * * @return \OCP\Files\SimpleFS\ISimpleFolder * @throws NotPermittedException * @throws \RuntimeException */ public function getCacheFolder() { $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); try { $folder = $this->appData->getFolder($cacheBusterValue); } catch (NotFoundException $e) { $folder = $this->appData->newFolder($cacheBusterValue); $this->cleanup(); } return $folder; } /** * Get a file from AppData * * @param string $filename * @throws NotFoundException * @return \OCP\Files\SimpleFS\ISimpleFile */ public function getCachedImage($filename) { $currentFolder = $this->getCacheFolder(); return $currentFolder->getFile($filename); } /** * Store a file for theming in AppData * * @param string $filename * @param string $data * @return \OCP\Files\SimpleFS\ISimpleFile */ public function setCachedImage($filename, $data) { $currentFolder = $this->getCacheFolder(); if ($currentFolder->fileExists($filename)) { $file = $currentFolder->getFile($filename); } else { $file = $currentFolder->newFile($filename); } $file->putContent($data); return $file; } /** * remove cached files that are not required any longer */ public function cleanup() { $currentFolder = $this->getCacheFolder(); $folders = $this->appData->getDirectoryListing(); foreach ($folders as $folder) { if ($folder->getName() !== 'images' && $folder->getName() !== $currentFolder->getName()) { $folder->delete(); } } } } Capabilities.php 0000604 00000001676 15247100611 0007651 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2016, ownCloud GmbH * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV; use OCP\Capabilities\ICapability; class Capabilities implements ICapability { public function getCapabilities() { return [ 'dav' => [ 'chunking' => '1.0', ] ]; } } Util.php 0000604 00000012173 15247100611 0006167 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net> * * @author Julius Haertl <jus@bitgrid.net> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Theming; use OCP\App\AppPathNotFoundException; use OCP\App\IAppManager; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\IConfig; use OCP\Files\IRootFolder; use Leafo\ScssPhp\Compiler; class Util { /** @var IConfig */ private $config; /** @var IAppManager */ private $appManager; /** @var IAppData */ private $appData; /** * Util constructor. * * @param IConfig $config * @param IAppManager $appManager * @param IAppData $appData */ public function __construct(IConfig $config, IAppManager $appManager, IAppData $appData) { $this->config = $config; $this->appManager = $appManager; $this->appData = $appData; } /** * @param string $color rgb color value * @return bool */ public function invertTextColor($color) { $l = $this->calculateLuminance($color); if($l>0.5) { return true; } else { return false; } } /** * get color for on-page elements: * theme color by default, grey if theme color is to bright * @param $color * @return string */ public function elementColor($color) { $l = $this->calculateLuminance($color); if($l>0.8) { return '#555555'; } else { return $color; } } /** * @param string $color rgb color value * @return float */ public function calculateLuminance($color) { $hex = preg_replace("/[^0-9A-Fa-f]/", '', $color); if (strlen($hex) === 3) { $hex = $hex{0} . $hex{0} . $hex{1} . $hex{1} . $hex{2} . $hex{2}; } if (strlen($hex) !== 6) { return 0; } $red = hexdec(substr($hex, 0, 2)); $green = hexdec(substr($hex, 2, 2)); $blue = hexdec(substr($hex, 4, 2)); $compiler = new Compiler(); $hsl = $compiler->toHSL($red, $green, $blue); return $hsl[3]/100; } /** * @param $color * @return string base64 encoded radio button svg */ public function generateRadioButton($color) { $radioButtonIcon = '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">' . '<path d="M8 1a7 7 0 0 0-7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0-7-7zm0 1a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6zm0 2a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" fill="'.$color.'"/></svg>'; return base64_encode($radioButtonIcon); } /** * @param $app string app name * @return string|ISimpleFile path to app icon / file of logo */ public function getAppIcon($app) { $app = str_replace(array('\0', '/', '\\', '..'), '', $app); try { $appPath = $this->appManager->getAppPath($app); $icon = $appPath . '/img/' . $app . '.svg'; if (file_exists($icon)) { return $icon; } $icon = $appPath . '/img/app.svg'; if (file_exists($icon)) { return $icon; } } catch (AppPathNotFoundException $e) {} if ($this->config->getAppValue('theming', 'logoMime', '') !== '') { $logoFile = null; try { $folder = $this->appData->getFolder('images'); if ($folder !== null) { return $folder->getFile('logo'); } } catch (NotFoundException $e) {} } return \OC::$SERVERROOT . '/core/img/logo.svg'; } /** * @param $app string app name * @param $image string relative path to image in app folder * @return string|false absolute path to image */ public function getAppImage($app, $image) { $app = str_replace(array('\0', '/', '\\', '..'), '', $app); $image = str_replace(array('\0', '\\', '..'), '', $image); if ($app === "core") { $icon = \OC::$SERVERROOT . '/core/img/' . $image; if (file_exists($icon)) { return $icon; } } try { $appPath = $this->appManager->getAppPath($app); } catch (AppPathNotFoundException $e) { return false; } $icon = $appPath . '/img/' . $image; if (file_exists($icon)) { return $icon; } $icon = $appPath . '/img/' . $image . '.svg'; if (file_exists($icon)) { return $icon; } $icon = $appPath . '/img/' . $image . '.png'; if (file_exists($icon)) { return $icon; } $icon = $appPath . '/img/' . $image . '.gif'; if (file_exists($icon)) { return $icon; } $icon = $appPath . '/img/' . $image . '.jpg'; if (file_exists($icon)) { return $icon; } return false; } /** * replace default color with a custom one * * @param $svg string content of a svg file * @param $color string color to match * @return string */ public function colorizeSvg($svg, $color) { $svg = preg_replace('/#0082c9/i', $color, $svg); return $svg; } } IconBuilder.php 0000604 00000013527 15247100611 0007455 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net> * * @author Julius Härtl <jus@bitgrid.net> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Theming; use Imagick; use ImagickPixel; use OCP\App\AppPathNotFoundException; use OCP\Files\SimpleFS\ISimpleFile; class IconBuilder { /** @var ThemingDefaults */ private $themingDefaults; /** @var Util */ private $util; /** * IconBuilder constructor. * * @param ThemingDefaults $themingDefaults * @param Util $util */ public function __construct( ThemingDefaults $themingDefaults, Util $util ) { $this->themingDefaults = $themingDefaults; $this->util = $util; } /** * @param $app string app name * @return string|false image blob */ public function getFavicon($app) { try { $icon = $this->renderAppIcon($app, 32); if ($icon === false) { return false; } $icon->setImageFormat("png24"); $data = $icon->getImageBlob(); $icon->destroy(); return $data; } catch (\ImagickException $e) { return false; } } /** * @param $app string app name * @return string|false image blob */ public function getTouchIcon($app) { try { $icon = $this->renderAppIcon($app, 512); if ($icon === false) { return false; } $icon->setImageFormat("png24"); $data = $icon->getImageBlob(); $icon->destroy(); return $data; } catch (\ImagickException $e) { return false; } } /** * Render app icon on themed background color * fallback to logo * * @param $app string app name * @param $size int size of the icon in px * @return Imagick|false */ public function renderAppIcon($app, $size) { $appIcon = $this->util->getAppIcon($app); if($appIcon === false) { return false; } if ($appIcon instanceof ISimpleFile) { $appIconContent = $appIcon->getContent(); $mime = $appIcon->getMimeType(); } else { $appIconContent = file_get_contents($appIcon); $mime = mime_content_type($appIcon); } if($appIconContent === false || $appIconContent === "") { return false; } $color = $this->themingDefaults->getColorPrimary(); // generate background image with rounded corners $background = '<?xml version="1.0" encoding="UTF-8"?>' . '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink">' . '<rect x="0" y="0" rx="100" ry="100" width="512" height="512" style="fill:' . $color . ';" />' . '</svg>'; // resize svg magic as this seems broken in Imagemagick if($mime === "image/svg+xml" || substr($appIconContent, 0, 4) === "<svg") { if(substr($appIconContent, 0, 5) !== "<?xml") { $svg = "<?xml version=\"1.0\"?>".$appIconContent; } else { $svg = $appIconContent; } $tmp = new Imagick(); $tmp->readImageBlob($svg); $x = $tmp->getImageWidth(); $y = $tmp->getImageHeight(); $res = $tmp->getImageResolution(); $tmp->destroy(); if($x>$y) { $max = $x; } else { $max = $y; } // convert svg to resized image $appIconFile = new Imagick(); $resX = (int)(512 * $res['x'] / $max * 2.53); $resY = (int)(512 * $res['y'] / $max * 2.53); $appIconFile->setResolution($resX, $resY); $appIconFile->setBackgroundColor(new ImagickPixel('transparent')); $appIconFile->readImageBlob($svg); $appIconFile->scaleImage(512, 512, true); } else { $appIconFile = new Imagick(); $appIconFile->setBackgroundColor(new ImagickPixel('transparent')); $appIconFile->readImageBlob($appIconContent); $appIconFile->scaleImage(512, 512, true); } // offset for icon positioning $border_w = (int)($appIconFile->getImageWidth() * 0.05); $border_h = (int)($appIconFile->getImageHeight() * 0.05); $innerWidth = (int)($appIconFile->getImageWidth() - $border_w * 2); $innerHeight = (int)($appIconFile->getImageHeight() - $border_h * 2); $appIconFile->adaptiveResizeImage($innerWidth, $innerHeight); // center icon $offset_w = 512 / 2 - $innerWidth / 2; $offset_h = 512 / 2 - $innerHeight / 2; $appIconFile->setImageFormat("png24"); $finalIconFile = new Imagick(); $finalIconFile->setBackgroundColor(new ImagickPixel('transparent')); $finalIconFile->readImageBlob($background); $finalIconFile->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT); $finalIconFile->setImageArtifact('compose:args', "1,0,-0.5,0.5"); $finalIconFile->compositeImage($appIconFile, Imagick::COMPOSITE_ATOP, $offset_w, $offset_h); $finalIconFile->setImageFormat('png24'); if (defined("Imagick::INTERPOLATE_BICUBIC") === true) { $filter = Imagick::INTERPOLATE_BICUBIC; } else { $filter = Imagick::FILTER_LANCZOS; } $finalIconFile->resizeImage($size, $size, $filter, 1, false); $appIconFile->destroy(); return $finalIconFile; } public function colorSvg($app, $image) { try { $imageFile = $this->util->getAppImage($app, $image); } catch (AppPathNotFoundException $e) { return false; } $svg = file_get_contents($imageFile); if ($svg !== false && $svg !== "") { $color = $this->util->elementColor($this->themingDefaults->getColorPrimary()); $svg = $this->util->colorizeSvg($svg, $color); return $svg; } else { return false; } } } Db/AccessToken.php 0000604 00000003136 15247100613 0010002 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\OAuth2\Db; use OCP\AppFramework\Db\Entity; /** * @method int getTokenId() * @method void setTokenId(int $identifier) * @method int getClientId() * @method void setClientId(int $identifier) * @method string getEncryptedToken() * @method void setEncryptedToken(string $token) * @method string getHashedCode() * @method void setHashedCode(string $token) */ class AccessToken extends Entity { /** @var int */ protected $tokenId; /** @var int */ protected $clientId; /** @var string */ protected $hashedCode; /** @var string */ protected $encryptedToken; public function __construct() { $this->addType('id', 'int'); $this->addType('token_id', 'int'); $this->addType('client_id', 'int'); $this->addType('hashed_code', 'string'); $this->addType('encrypted_token', 'string'); } } Db/ClientMapper.php 0000604 00000004544 15247100613 0010167 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\OAuth2\Db; use OCA\OAuth2\Exceptions\ClientNotFoundException; use OCP\AppFramework\Db\Mapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; class ClientMapper extends Mapper { /** * @param IDBConnection $db */ public function __construct(IDBConnection $db) { parent::__construct($db, 'oauth2_clients'); } /** * @param string $clientIdentifier * @return Client * @throws ClientNotFoundException */ public function getByIdentifier($clientIdentifier) { $qb = $this->db->getQueryBuilder(); $qb ->select('*') ->from($this->tableName) ->where($qb->expr()->eq('client_identifier', $qb->createNamedParameter($clientIdentifier))); $result = $qb->execute(); $row = $result->fetch(); $result->closeCursor(); if($row === false) { throw new ClientNotFoundException(); } return Client::fromRow($row); } /** * @param string $uid internal uid of the client * @return Client * @throws ClientNotFoundException */ public function getByUid($uid) { $qb = $this->db->getQueryBuilder(); $qb ->select('*') ->from($this->tableName) ->where($qb->expr()->eq('id', $qb->createNamedParameter($uid, IQueryBuilder::PARAM_INT))); $result = $qb->execute(); $row = $result->fetch(); $result->closeCursor(); if($row === false) { throw new ClientNotFoundException(); } return Client::fromRow($row); } /** * @return Client[] */ public function getClients() { $qb = $this->db->getQueryBuilder(); $qb ->select('*') ->from($this->tableName); return $this->findEntities($qb->getSQL()); } } Db/AccessTokenMapper.php 0000604 00000003727 15247100613 0011155 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\OAuth2\Db; use OCA\OAuth2\Exceptions\AccessTokenNotFoundException; use OCP\AppFramework\Db\Mapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; class AccessTokenMapper extends Mapper { /** * @param IDBConnection $db */ public function __construct(IDBConnection $db) { parent::__construct($db, 'oauth2_access_tokens'); } /** * @param string $code * @return AccessToken * @throws AccessTokenNotFoundException */ public function getByCode($code) { $qb = $this->db->getQueryBuilder(); $qb ->select('*') ->from($this->tableName) ->where($qb->expr()->eq('hashed_code', $qb->createNamedParameter(hash('sha512', $code)))); $result = $qb->execute(); $row = $result->fetch(); $result->closeCursor(); if($row === false) { throw new AccessTokenNotFoundException(); } return AccessToken::fromRow($row); } /** * delete all access token from a given client * * @param int $id */ public function deleteByClientId($id) { $qb = $this->db->getQueryBuilder(); $qb ->delete($this->tableName) ->where($qb->expr()->eq('client_id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $qb->execute(); } } Db/Client.php 0000604 00000003150 15247100613 0007012 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\OAuth2\Db; use OCP\AppFramework\Db\Entity; /** * @method string getClientIdentifier() * @method void setClientIdentifier(string $identifier) * @method string getSecret() * @method void setSecret(string $secret) * @method string getRedirectUri() * @method void setRedirectUri(string $redirectUri) * @method string getName() * @method void setName(string $name) */ class Client extends Entity { /** @var string */ protected $name; /** @var string */ protected $redirectUri; /** @var string */ protected $clientIdentifier; /** @var string */ protected $secret; public function __construct() { $this->addType('id', 'int'); $this->addType('name', 'string'); $this->addType('redirect_uri', 'string'); $this->addType('client_identifier', 'string'); $this->addType('secret', 'string'); } } Exceptions/ClientNotFoundException.php 0000604 00000001602 15247100613 0014142 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\OAuth2\Exceptions; class ClientNotFoundException extends \Exception {} Exceptions/AccessTokenNotFoundException.php 0000604 00000001607 15247100613 0015133 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\OAuth2\Exceptions; class AccessTokenNotFoundException extends \Exception {} Controller/SettingsController.php 0000604 00000002764 15247100613 0013250 0 ustar 00 <?php /** * * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Controller; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; use OCP\Util; class SettingsController extends Controller { public function __construct($appName, IRequest $request) { parent::__construct($appName, $request); } /** * @param string $maxUploadSize * @return JSONResponse */ public function setMaxUploadSize($maxUploadSize) { $setMaxSize = \OC_Files::setUploadLimit(Util::computerFileSize($maxUploadSize)); if ($setMaxSize === false) { return new JSONResponse([], Http::STATUS_BAD_REQUEST); } else { return new JSONResponse([ 'maxUploadSize' => Util::humanFileSize($setMaxSize) ]); } } } Controller/LoginRedirectorController.php 0000604 00000004305 15247100613 0014534 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\OAuth2\Controller; use OCA\OAuth2\Db\ClientMapper; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\RedirectResponse; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; class LoginRedirectorController extends Controller { /** @var IURLGenerator */ private $urlGenerator; /** @var ClientMapper */ private $clientMapper; /** @var ISession */ private $session; /** * @param string $appName * @param IRequest $request * @param IURLGenerator $urlGenerator * @param ClientMapper $clientMapper * @param ISession $session */ public function __construct($appName, IRequest $request, IURLGenerator $urlGenerator, ClientMapper $clientMapper, ISession $session) { parent::__construct($appName, $request); $this->urlGenerator = $urlGenerator; $this->clientMapper = $clientMapper; $this->session = $session; } /** * @PublicPage * @NoCSRFRequired * @UseSession * * @param string $client_id * @param string $state * @return RedirectResponse */ public function authorize($client_id, $state) { $client = $this->clientMapper->getByIdentifier($client_id); $this->session->set('oauth.state', $state); $targetUrl = $this->urlGenerator->linkToRouteAbsolute( 'core.ClientFlowLogin.showAuthPickerPage', [ 'clientIdentifier' => $client->getClientIdentifier(), ] ); return new RedirectResponse($targetUrl); } } Controller/OauthApiController.php 0000604 00000005362 15247100613 0013157 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\OAuth2\Controller; use OC\Authentication\Token\DefaultTokenMapper; use OCA\OAuth2\Db\AccessTokenMapper; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; class OauthApiController extends Controller { /** @var AccessTokenMapper */ private $accessTokenMapper; /** @var ICrypto */ private $crypto; /** @var DefaultTokenMapper */ private $defaultTokenMapper; /** @var ISecureRandom */ private $secureRandom; /** * @param string $appName * @param IRequest $request * @param ICrypto $crypto * @param AccessTokenMapper $accessTokenMapper * @param DefaultTokenMapper $defaultTokenMapper * @param ISecureRandom $secureRandom */ public function __construct($appName, IRequest $request, ICrypto $crypto, AccessTokenMapper $accessTokenMapper, DefaultTokenMapper $defaultTokenMapper, ISecureRandom $secureRandom) { parent::__construct($appName, $request); $this->crypto = $crypto; $this->accessTokenMapper = $accessTokenMapper; $this->defaultTokenMapper = $defaultTokenMapper; $this->secureRandom = $secureRandom; } /** * @PublicPage * @NoCSRFRequired * * @param string $code * @return JSONResponse */ public function getToken($code) { $accessToken = $this->accessTokenMapper->getByCode($code); $decryptedToken = $this->crypto->decrypt($accessToken->getEncryptedToken(), $code); $newCode = $this->secureRandom->generate(128); $accessToken->setHashedCode(hash('sha512', $newCode)); $accessToken->setEncryptedToken($this->crypto->encrypt($decryptedToken, $newCode)); $this->accessTokenMapper->update($accessToken); return new JSONResponse( [ 'access_token' => $decryptedToken, 'token_type' => 'Bearer', 'expires_in' => 3600, 'refresh_token' => $newCode, 'user_id' => $this->defaultTokenMapper->getTokenById($accessToken->getTokenId())->getUID(), ] ); } } BackgroundJob/CleanupFileLocks.php 0000604 00000003003 15247115235 0013147 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\BackgroundJob; use OC\BackgroundJob\TimedJob; use OC\Lock\DBLockingProvider; /** * Clean up all file locks that are expired for the DB file locking provider */ class CleanupFileLocks extends TimedJob { /** * Default interval in minutes * * @var int $defaultIntervalMin **/ protected $defaultIntervalMin = 5; /** * sets the correct interval for this timed job */ public function __construct() { $this->interval = $this->defaultIntervalMin * 60; } /** * Makes the background job do its work * * @param array $argument unused argument */ public function run($argument) { $lockingProvider = \OC::$server->getLockingProvider(); if($lockingProvider instanceof DBLockingProvider) { $lockingProvider->cleanExpiredLocks(); } } } BackgroundJob/DeleteOrphanedItems.php 0000604 00000010704 15247115235 0013657 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\BackgroundJob; use OC\BackgroundJob\TimedJob; use OCP\DB\QueryBuilder\IQueryBuilder; /** * Delete all share entries that have no matching entries in the file cache table. */ class DeleteOrphanedItems extends TimedJob { const CHUNK_SIZE = 200; /** @var \OCP\IDBConnection */ protected $connection; /** @var \OCP\ILogger */ protected $logger; /** * Default interval in minutes * * @var int $defaultIntervalMin **/ protected $defaultIntervalMin = 60; /** * sets the correct interval for this timed job */ public function __construct() { $this->interval = $this->defaultIntervalMin * 60; $this->connection = \OC::$server->getDatabaseConnection(); $this->logger = \OC::$server->getLogger(); } /** * Makes the background job do its work * * @param array $argument unused argument */ public function run($argument) { $this->cleanSystemTags(); $this->cleanUserTags(); $this->cleanComments(); $this->cleanCommentMarkers(); } /** * Deleting orphaned system tag mappings * * @param string $table * @param string $idCol * @param string $typeCol * @return int Number of deleted entries */ protected function cleanUp($table, $idCol, $typeCol) { $deletedEntries = 0; $query = $this->connection->getQueryBuilder(); $query->select('t1.' . $idCol) ->from($table, 't1') ->where($query->expr()->eq($typeCol, $query->expr()->literal('files'))) ->andWhere($query->expr()->isNull('t2.fileid')) ->leftJoin('t1', 'filecache', 't2', $query->expr()->eq($query->expr()->castColumn('t1.' . $idCol, IQueryBuilder::PARAM_INT), 't2.fileid')) ->groupBy('t1.' . $idCol) ->setMaxResults(self::CHUNK_SIZE); $deleteQuery = $this->connection->getQueryBuilder(); $deleteQuery->delete($table) ->where($deleteQuery->expr()->eq($idCol, $deleteQuery->createParameter('objectid'))); $deletedInLastChunk = self::CHUNK_SIZE; while ($deletedInLastChunk === self::CHUNK_SIZE) { $result = $query->execute(); $deletedInLastChunk = 0; while ($row = $result->fetch()) { $deletedInLastChunk++; $deletedEntries += $deleteQuery->setParameter('objectid', (int) $row[$idCol]) ->execute(); } $result->closeCursor(); } return $deletedEntries; } /** * Deleting orphaned system tag mappings * * @return int Number of deleted entries */ protected function cleanSystemTags() { $deletedEntries = $this->cleanUp('systemtag_object_mapping', 'objectid', 'objecttype'); $this->logger->debug("$deletedEntries orphaned system tag relations deleted", ['app' => 'DeleteOrphanedItems']); return $deletedEntries; } /** * Deleting orphaned user tag mappings * * @return int Number of deleted entries */ protected function cleanUserTags() { $deletedEntries = $this->cleanUp('vcategory_to_object', 'objid', 'type'); $this->logger->debug("$deletedEntries orphaned user tag relations deleted", ['app' => 'DeleteOrphanedItems']); return $deletedEntries; } /** * Deleting orphaned comments * * @return int Number of deleted entries */ protected function cleanComments() { $deletedEntries = $this->cleanUp('comments', 'object_id', 'object_type'); $this->logger->debug("$deletedEntries orphaned comments deleted", ['app' => 'DeleteOrphanedItems']); return $deletedEntries; } /** * Deleting orphaned comment read markers * * @return int Number of deleted entries */ protected function cleanCommentMarkers() { $deletedEntries = $this->cleanUp('comments_read_markers', 'object_id', 'object_type'); $this->logger->debug("$deletedEntries orphaned comment read marks deleted", ['app' => 'DeleteOrphanedItems']); return $deletedEntries; } } BackgroundJob/ScanFiles.php 0000604 00000006042 15247115235 0011641 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\BackgroundJob; use OC\Files\Utils\Scanner; use OCP\IConfig; use OCP\IDBConnection; use OCP\ILogger; use OCP\IUser; use OCP\IUserManager; /** * Class ScanFiles is a background job used to run the file scanner over the user * accounts to ensure integrity of the file cache. * * @package OCA\Files\BackgroundJob */ class ScanFiles extends \OC\BackgroundJob\TimedJob { /** @var IConfig */ private $config; /** @var IUserManager */ private $userManager; /** @var IDBConnection */ private $dbConnection; /** @var ILogger */ private $logger; /** Amount of users that should get scanned per execution */ const USERS_PER_SESSION = 500; /** * @param IConfig|null $config * @param IUserManager|null $userManager * @param IDBConnection|null $dbConnection * @param ILogger|null $logger */ public function __construct(IConfig $config = null, IUserManager $userManager = null, IDBConnection $dbConnection = null, ILogger $logger = null) { // Run once per 10 minutes $this->setInterval(60 * 10); if (is_null($userManager) || is_null($config)) { $this->fixDIForJobs(); } else { $this->config = $config; $this->userManager = $userManager; $this->logger = $logger; } } protected function fixDIForJobs() { $this->config = \OC::$server->getConfig(); $this->userManager = \OC::$server->getUserManager(); $this->logger = \OC::$server->getLogger(); } /** * @param IUser $user */ protected function runScanner(IUser $user) { try { $scanner = new Scanner( $user->getUID(), $this->dbConnection, $this->logger ); $scanner->backgroundScan(''); } catch (\Exception $e) { $this->logger->logException($e, ['app' => 'files']); } \OC_Util::tearDownFS(); } /** * @param $argument * @throws \Exception */ protected function run($argument) { $offset = $this->config->getAppValue('files', 'cronjob_scan_files', 0); $users = $this->userManager->search('', self::USERS_PER_SESSION, $offset); if (!count($users)) { // No users found, reset offset and retry $offset = 0; $users = $this->userManager->search('', self::USERS_PER_SESSION); } $offset += self::USERS_PER_SESSION; $this->config->setAppValue('files', 'cronjob_scan_files', $offset); foreach ($users as $user) { $this->runScanner($user); } } } App.php 0000604 00000003133 15247115235 0005776 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christopher Schäpers <kondou@ts.unde.re> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files; class App { /** * @var \OCP\INavigationManager */ private static $navigationManager; /** * Returns the app's navigation manager * * @return \OCP\INavigationManager */ public static function getNavigationManager() { // TODO: move this into a service in the Application class if (self::$navigationManager === null) { self::$navigationManager = new \OC\NavigationManager( \OC::$server->getAppManager(), \OC::$server->getURLGenerator(), \OC::$server->getL10NFactory(), \OC::$server->getUserSession(), \OC::$server->getGroupManager(), \OC::$server->getConfig() ); self::$navigationManager->clear(false); } return self::$navigationManager; } } Controller/ViewController.php 0000604 00000020240 15247115235 0012355 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\Controller; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUserSession; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use OCP\Files\Folder; use OCP\App\IAppManager; use Symfony\Component\EventDispatcher\GenericEvent; /** * Class ViewController * * @package OCA\Files\Controller */ class ViewController extends Controller { /** @var string */ protected $appName; /** @var IRequest */ protected $request; /** @var IURLGenerator */ protected $urlGenerator; /** @var IL10N */ protected $l10n; /** @var IConfig */ protected $config; /** @var EventDispatcherInterface */ protected $eventDispatcher; /** @var IUserSession */ protected $userSession; /** @var IAppManager */ protected $appManager; /** @var IRootFolder */ protected $rootFolder; /** * @param string $appName * @param IRequest $request * @param IURLGenerator $urlGenerator * @param IL10N $l10n * @param IConfig $config * @param EventDispatcherInterface $eventDispatcherInterface * @param IUserSession $userSession * @param IAppManager $appManager * @param IRootFolder $rootFolder */ public function __construct($appName, IRequest $request, IURLGenerator $urlGenerator, IL10N $l10n, IConfig $config, EventDispatcherInterface $eventDispatcherInterface, IUserSession $userSession, IAppManager $appManager, IRootFolder $rootFolder ) { parent::__construct($appName, $request); $this->appName = $appName; $this->request = $request; $this->urlGenerator = $urlGenerator; $this->l10n = $l10n; $this->config = $config; $this->eventDispatcher = $eventDispatcherInterface; $this->userSession = $userSession; $this->appManager = $appManager; $this->rootFolder = $rootFolder; } /** * @param string $appName * @param string $scriptName * @return string */ protected function renderScript($appName, $scriptName) { $content = ''; $appPath = \OC_App::getAppPath($appName); $scriptPath = $appPath . '/' . $scriptName; if (file_exists($scriptPath)) { // TODO: sanitize path / script name ? ob_start(); include $scriptPath; $content = ob_get_contents(); @ob_end_clean(); } return $content; } /** * FIXME: Replace with non static code * * @return array * @throws \OCP\Files\NotFoundException */ protected function getStorageInfo() { $dirInfo = \OC\Files\Filesystem::getFileInfo('/', false); return \OC_Helper::getStorageInfo('/', $dirInfo); } /** * @NoCSRFRequired * @NoAdminRequired * * @param string $dir * @param string $view * @param string $fileid * @return TemplateResponse|RedirectResponse */ public function index($dir = '', $view = '', $fileid = null, $fileNotFound = false) { if ($fileid !== null) { try { return $this->showFile($fileid); } catch (NotFoundException $e) { return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', ['fileNotFound' => true])); } } $nav = new \OCP\Template('files', 'appnavigation', ''); // Load the files we need \OCP\Util::addStyle('files', 'merged'); \OCP\Util::addScript('files', 'merged-index'); // mostly for the home storage's free space // FIXME: Make non static $storageInfo = $this->getStorageInfo(); \OCA\Files\App::getNavigationManager()->add( [ 'id' => 'favorites', 'appname' => 'files', 'script' => 'simplelist.php', 'order' => 5, 'name' => $this->l10n->t('Favorites') ] ); $navItems = \OCA\Files\App::getNavigationManager()->getAll(); usort($navItems, function($item1, $item2) { return $item1['order'] - $item2['order']; }); $nav->assign('navigationItems', $navItems); $nav->assign('usage', \OC_Helper::humanFileSize($storageInfo['used'])); if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) { $totalSpace = $this->l10n->t('Unlimited'); } else { $totalSpace = \OC_Helper::humanFileSize($storageInfo['total']); } $nav->assign('total_space', $totalSpace); $nav->assign('quota', $storageInfo['quota']); $nav->assign('usage_relative', $storageInfo['relative']); $contentItems = []; // render the container content for every navigation item foreach ($navItems as $item) { $content = ''; if (isset($item['script'])) { $content = $this->renderScript($item['appname'], $item['script']); } $contentItem = []; $contentItem['id'] = $item['id']; $contentItem['content'] = $content; $contentItems[] = $contentItem; } $event = new GenericEvent(null, ['hiddenFields' => []]); $this->eventDispatcher->dispatch('OCA\Files::loadAdditionalScripts', $event); $params = []; $params['usedSpacePercent'] = (int)$storageInfo['relative']; $params['owner'] = $storageInfo['owner']; $params['ownerDisplayName'] = $storageInfo['ownerDisplayName']; $params['isPublic'] = false; $params['allowShareWithLink'] = $this->config->getAppValue('core', 'shareapi_allow_links', 'yes'); $user = $this->userSession->getUser()->getUID(); $params['defaultFileSorting'] = $this->config->getUserValue($user, 'files', 'file_sorting', 'name'); $params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc'); $showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false); $params['showHiddenFiles'] = $showHidden ? 1 : 0; $params['fileNotFound'] = $fileNotFound ? 1 : 0; $params['appNavigation'] = $nav; $params['appContents'] = $contentItems; $params['hiddenFields'] = $event->getArgument('hiddenFields'); $response = new TemplateResponse( $this->appName, 'index', $params ); $policy = new ContentSecurityPolicy(); $policy->addAllowedFrameDomain('\'self\''); $response->setContentSecurityPolicy($policy); return $response; } /** * Redirects to the file list and highlight the given file id * * @param string $fileId file id to show * @return RedirectResponse redirect response or not found response * @throws \OCP\Files\NotFoundException */ private function showFile($fileId) { $uid = $this->userSession->getUser()->getUID(); $baseFolder = $this->rootFolder->getUserFolder($uid); $files = $baseFolder->getById($fileId); $params = []; if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) { $baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/'); $files = $baseFolder->getById($fileId); $params['view'] = 'trashbin'; } if (!empty($files)) { $file = current($files); if ($file instanceof Folder) { // set the full path to enter the folder $params['dir'] = $baseFolder->getRelativePath($file->getPath()); } else { // set parent path as dir $params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath()); // and scroll to the entry $params['scrollto'] = $file->getName(); } return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params)); } throw new \OCP\Files\NotFoundException(); } } Controller/ApiController.php 0000604 00000016420 15247115235 0012161 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tobias Kaminsky <tobias@kaminsky.me> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Controller; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IRequest; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\Response; use OCA\Files\Service\TagService; use OCP\IPreview; use OCP\Share\IManager; use OC\Files\Node\Node; use OCP\IUserSession; /** * Class ApiController * * @package OCA\Files\Controller */ class ApiController extends Controller { /** @var TagService */ private $tagService; /** @var IManager **/ private $shareManager; /** @var IPreview */ private $previewManager; /** IUserSession */ private $userSession; /** IConfig */ private $config; /** @var Folder */ private $userFolder; /** * @param string $appName * @param IRequest $request * @param IUserSession $userSession * @param TagService $tagService * @param IPreview $previewManager * @param IManager $shareManager * @param IConfig $config * @param Folder $userFolder */ public function __construct($appName, IRequest $request, IUserSession $userSession, TagService $tagService, IPreview $previewManager, IManager $shareManager, IConfig $config, Folder $userFolder) { parent::__construct($appName, $request); $this->userSession = $userSession; $this->tagService = $tagService; $this->previewManager = $previewManager; $this->shareManager = $shareManager; $this->config = $config; $this->userFolder = $userFolder; } /** * Gets a thumbnail of the specified file * * @since API version 1.0 * * @NoAdminRequired * @NoCSRFRequired * @StrictCookieRequired * * @param int $x * @param int $y * @param string $file URL-encoded filename * @return DataResponse|FileDisplayResponse */ public function getThumbnail($x, $y, $file) { if($x < 1 || $y < 1) { return new DataResponse(['message' => 'Requested size must be numeric and a positive value.'], Http::STATUS_BAD_REQUEST); } try { $file = $this->userFolder->get($file); if ($file instanceof Folder) { throw new NotFoundException(); } /** @var File $file */ $preview = $this->previewManager->getPreview($file, $x, $y, true); return new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => $preview->getMimeType()]); } catch (NotFoundException $e) { return new DataResponse(['message' => 'File not found.'], Http::STATUS_NOT_FOUND); } catch (\Exception $e) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } } /** * Updates the info of the specified file path * The passed tags are absolute, which means they will * replace the actual tag selection. * * @NoAdminRequired * * @param string $path path * @param array|string $tags array of tags * @return DataResponse */ public function updateFileTags($path, $tags = null) { $result = []; // if tags specified or empty array, update tags if (!is_null($tags)) { try { $this->tagService->updateFileTags($path, $tags); } catch (\OCP\Files\NotFoundException $e) { return new DataResponse([ 'message' => $e->getMessage() ], Http::STATUS_NOT_FOUND); } catch (\OCP\Files\StorageNotAvailableException $e) { return new DataResponse([ 'message' => $e->getMessage() ], Http::STATUS_SERVICE_UNAVAILABLE); } catch (\Exception $e) { return new DataResponse([ 'message' => $e->getMessage() ], Http::STATUS_NOT_FOUND); } $result['tags'] = $tags; } return new DataResponse($result); } /** * @param \OCP\Files\Node[] $nodes * @return array */ private function formatNodes(array $nodes) { return array_values(array_map(function (Node $node) { /** @var \OC\Files\Node\Node $shareTypes */ $shareTypes = $this->getShareTypes($node); $file = \OCA\Files\Helper::formatFileInfo($node->getFileInfo()); $parts = explode('/', dirname($node->getPath()), 4); if (isset($parts[3])) { $file['path'] = '/' . $parts[3]; } else { $file['path'] = '/'; } if (!empty($shareTypes)) { $file['shareTypes'] = $shareTypes; } return $file; }, $nodes)); } /** * Returns a list of recently modifed files. * * @NoAdminRequired * * @return DataResponse */ public function getRecentFiles() { $nodes = $this->userFolder->getRecent(100); $files = $this->formatNodes($nodes); return new DataResponse(['files' => $files]); } /** * Return a list of share types for outgoing shares * * @param Node $node file node * * @return int[] array of share types */ private function getShareTypes(Node $node) { $userId = $this->userSession->getUser()->getUID(); $shareTypes = []; $requestedShareTypes = [ \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL ]; foreach ($requestedShareTypes as $requestedShareType) { // one of each type is enough to find out about the types $shares = $this->shareManager->getSharesBy( $userId, $requestedShareType, $node, false, 1 ); if (!empty($shares)) { $shareTypes[] = $requestedShareType; } } return $shareTypes; } /** * Change the default sort mode * * @NoAdminRequired * * @param string $mode * @param string $direction * @return Response */ public function updateFileSorting($mode, $direction) { $allowedMode = ['name', 'size', 'mtime']; $allowedDirection = ['asc', 'desc']; if (!in_array($mode, $allowedMode) || !in_array($direction, $allowedDirection)) { $response = new Response(); $response->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY); return $response; } $this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'file_sorting', $mode); $this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'file_sorting_direction', $direction); return new Response(); } /** * Toggle default for showing/hiding hidden files * * @NoAdminRequired * * @param bool $show */ public function showHiddenFiles($show) { $this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', (int) $show); return new Response(); } } Activity/Filter/FileChanges.php 0000604 00000004344 15247115235 0012454 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity\Filter; use OCP\Activity\IFilter; use OCP\IL10N; use OCP\IURLGenerator; class FileChanges implements IFilter { /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** * @param IL10N $l * @param IURLGenerator $url */ public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * @return string Lowercase a-z only identifier * @since 11.0.0 */ public function getIdentifier() { return 'files'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('File changes'); } /** * @return int * @since 11.0.0 */ public function getPriority() { return 30; } /** * @return string Full URL to an icon, empty string when none is given * @since 11.0.0 */ public function getIcon() { return $this->url->getAbsoluteURL($this->url->imagePath('core', 'places/files-dark.svg')); } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function filterTypes(array $types) { return array_intersect([ 'file_created', 'file_changed', 'file_deleted', 'file_restored', ], $types); } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function allowedApps() { return ['files']; } } Activity/Filter/Favorites.php 0000604 00000007516 15247115235 0012252 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity\Filter; use OCA\Files\Activity\Helper; use OCP\Activity\IFilter; use OCP\Activity\IManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IL10N; use OCP\IURLGenerator; class Favorites implements IFilter { /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** @var IManager */ protected $activityManager; /** @var Helper */ protected $helper; /** @var IDBConnection */ protected $db; /** * @param IL10N $l * @param IURLGenerator $url * @param IManager $activityManager * @param Helper $helper * @param IDBConnection $db */ public function __construct(IL10N $l, IURLGenerator $url, IManager $activityManager, Helper $helper, IDBConnection $db) { $this->l = $l; $this->url = $url; $this->activityManager = $activityManager; $this->helper = $helper; $this->db = $db; } /** * @return string Lowercase a-z only identifier * @since 11.0.0 */ public function getIdentifier() { return 'files_favorites'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('Favorites'); } /** * @return int * @since 11.0.0 */ public function getPriority() { return 10; } /** * @return string Full URL to an icon, empty string when none is given * @since 11.0.0 */ public function getIcon() { return $this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/star-dark.svg')); } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function filterTypes(array $types) { return array_intersect([ 'file_created', 'file_changed', 'file_deleted', 'file_restored', ], $types); } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function allowedApps() { return ['files']; } /** * @param IQueryBuilder $query */ public function filterFavorites(IQueryBuilder $query) { try { $user = $this->activityManager->getCurrentUserId(); } catch (\UnexpectedValueException $e) { return; } try { $favorites = $this->helper->getFavoriteFilePaths($user); } catch (\RuntimeException $e) { return; } $limitations = []; if (!empty($favorites['items'])) { $limitations[] = $query->expr()->in('file', $query->createNamedParameter($favorites['items'], IQueryBuilder::PARAM_STR_ARRAY)); } foreach ($favorites['folders'] as $favorite) { $limitations[] = $query->expr()->like('file', $query->createNamedParameter( $this->db->escapeLikeParameter($favorite . '/') . '%' )); } if (empty($limitations)) { return; } $function = $query->createFunction(' CASE WHEN ' . $query->getColumnName('app') . ' <> ' . $query->createNamedParameter('files') . ' THEN 1 WHEN ' . $query->getColumnName('app') . ' = ' . $query->createNamedParameter('files') . ' AND (' . implode(' OR ', $limitations) . ') THEN 1 END = 1' ); $query->andWhere($function); } } Activity/Provider.php 0000604 00000016577 15247115235 0010664 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Comments\Activity; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\Activity\IProvider; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory; class Provider implements IProvider { /** @var IFactory */ protected $languageFactory; /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** @var ICommentsManager */ protected $commentsManager; /** @var IUserManager */ protected $userManager; /** @var IManager */ protected $activityManager; /** @var string[] */ protected $displayNames = []; /** * @param IFactory $languageFactory * @param IURLGenerator $url * @param ICommentsManager $commentsManager * @param IUserManager $userManager * @param IManager $activityManager */ public function __construct(IFactory $languageFactory, IURLGenerator $url, ICommentsManager $commentsManager, IUserManager $userManager, IManager $activityManager) { $this->languageFactory = $languageFactory; $this->url = $url; $this->commentsManager = $commentsManager; $this->userManager = $userManager; $this->activityManager = $activityManager; } /** * @param string $language * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parse($language, IEvent $event, IEvent $previousEvent = null) { if ($event->getApp() !== 'comments') { throw new \InvalidArgumentException(); } $this->l = $this->languageFactory->get('comments', $language); if ($event->getSubject() === 'add_comment_subject') { $this->parseMessage($event); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg'))); } if ($this->activityManager->isFormattingFilteredObject()) { try { return $this->parseShortVersion($event); } catch (\InvalidArgumentException $e) { // Ignore and simply use the long version... } } return $this->parseLongVersion($event); } else { throw new \InvalidArgumentException(); } } /** * @param IEvent $event * @return IEvent * @throws \InvalidArgumentException */ protected function parseShortVersion(IEvent $event) { $subjectParameters = $this->getSubjectParameters($event); if ($event->getSubject() === 'add_comment_subject') { if ($subjectParameters['actor'] === $this->activityManager->getCurrentUserId()) { $event->setParsedSubject($this->l->t('You commented')) ->setRichSubject($this->l->t('You commented'), []); } else { $author = $this->generateUserParameter($subjectParameters['actor']); $event->setParsedSubject($this->l->t('%1$s commented', [$author['name']])) ->setRichSubject($this->l->t('{author} commented'), [ 'author' => $author, ]); } } else { throw new \InvalidArgumentException(); } return $event; } /** * @param IEvent $event * @return IEvent * @throws \InvalidArgumentException */ protected function parseLongVersion(IEvent $event) { $subjectParameters = $this->getSubjectParameters($event); if ($event->getSubject() === 'add_comment_subject') { if ($subjectParameters['actor'] === $this->activityManager->getCurrentUserId()) { $event->setParsedSubject($this->l->t('You commented on %1$s', [ $subjectParameters['filePath'], ])) ->setRichSubject($this->l->t('You commented on {file}'), [ 'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']), ]); } else { $author = $this->generateUserParameter($subjectParameters['actor']); $event->setParsedSubject($this->l->t('%1$s commented on %2$s', [ $author['name'], $subjectParameters['filePath'], ])) ->setRichSubject($this->l->t('{author} commented on {file}'), [ 'author' => $author, 'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']), ]); } } else { throw new \InvalidArgumentException(); } return $event; } protected function getSubjectParameters(IEvent $event) { $subjectParameters = $event->getSubjectParameters(); if (isset($subjectParameters['fileId'])) { return $subjectParameters; } // Fix subjects from 12.0.3 and older return [ 'actor' => $subjectParameters[0], 'fileId' => (int) $event->getObjectId(), 'filePath' => trim($subjectParameters[1], '/'), ]; } /** * @param IEvent $event */ protected function parseMessage(IEvent $event) { $messageParameters = $event->getMessageParameters(); if (empty($messageParameters)) { // Email return; } $commentId = isset($messageParameters['commentId']) ? $messageParameters['commentId'] : $messageParameters[0]; try { $comment = $this->commentsManager->get((string) $commentId); $message = $comment->getMessage(); $message = str_replace("\n", '<br />', str_replace(['<', '>'], ['<', '>'], $message)); $mentionCount = 1; $mentions = []; foreach ($comment->getMentions() as $mention) { if ($mention['type'] !== 'user') { continue; } $message = preg_replace( '/(^|\s)(' . '@' . $mention['id'] . ')(\b)/', //'${1}' . $this->regexSafeUser($mention['id'], $displayName) . '${3}', '${1}' . '{mention' . $mentionCount . '}' . '${3}', $message ); $mentions['mention' . $mentionCount] = $this->generateUserParameter($mention['id']); $mentionCount++; } $event->setParsedMessage($comment->getMessage()) ->setRichMessage($message, $mentions); } catch (NotFoundException $e) { } } /** * @param int $id * @param string $path * @return array */ protected function generateFileParameter($id, $path) { return [ 'type' => 'file', 'id' => $id, 'name' => basename($path), 'path' => $path, 'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]), ]; } /** * @param string $uid * @return array */ protected function generateUserParameter($uid) { if (!isset($this->displayNames[$uid])) { $this->displayNames[$uid] = $this->getDisplayName($uid); } return [ 'type' => 'user', 'id' => $uid, 'name' => $this->displayNames[$uid], ]; } /** * @param string $uid * @return string */ protected function getDisplayName($uid) { $user = $this->userManager->get($uid); if ($user instanceof IUser) { return $user->getDisplayName(); } else { return $uid; } } } Activity/Helper.php 0000604 00000004360 15247115235 0010274 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\Activity; use OCP\Files\Folder; use OCP\ITagManager; class Helper { /** If a user has a lot of favorites the query might get too slow and long */ const FAVORITE_LIMIT = 50; /** @var ITagManager */ protected $tagManager; /** * @param ITagManager $tagManager */ public function __construct(ITagManager $tagManager) { $this->tagManager = $tagManager; } /** * Returns an array with the favorites * * @param string $user * @return array * @throws \RuntimeException when too many or no favorites where found */ public function getFavoriteFilePaths($user) { $tags = $this->tagManager->load('files', [], false, $user); $favorites = $tags->getFavorites(); if (empty($favorites)) { throw new \RuntimeException('No favorites', 1); } else if (isset($favorites[self::FAVORITE_LIMIT])) { throw new \RuntimeException('Too many favorites', 2); } // Can not DI because the user is not known on instantiation $rootFolder = \OC::$server->getUserFolder($user); $folders = $items = []; foreach ($favorites as $favorite) { $nodes = $rootFolder->getById($favorite); if (!empty($nodes)) { /** @var \OCP\Files\Node $node */ $node = array_shift($nodes); $path = substr($node->getPath(), strlen($user . '/files/')); $items[] = $path; if ($node instanceof Folder) { $folders[] = $path; } } } if (empty($items)) { throw new \RuntimeException('No favorites', 1); } return [ 'items' => $items, 'folders' => $folders, ]; } } Activity/Settings/FileRestored.php 0000604 00000004423 15247115235 0013244 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity\Settings; use OCP\Activity\ISetting; use OCP\IL10N; class FileRestored implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'file_restored'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('A new file or folder has been <strong>restored</strong>'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 4; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } Activity/Settings/FileDeleted.php 0000604 00000004420 15247115235 0013020 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity\Settings; use OCP\Activity\ISetting; use OCP\IL10N; class FileDeleted implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'file_deleted'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('A new file or folder has been <strong>deleted</strong>'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 3; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } Activity/Settings/FileFavorite.php 0000604 00000004513 15247115235 0013234 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity\Settings; use OCP\Activity\ISetting; use OCP\IL10N; class FileFavorite implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'file_favorite'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 2; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return false; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return false; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } Activity/Settings/FileCreated.php 0000604 00000004420 15247115235 0013021 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity\Settings; use OCP\Activity\ISetting; use OCP\IL10N; class FileCreated implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'file_created'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('A new file or folder has been <strong>created</strong>'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 0; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } Activity/Settings/FileChanged.php 0000604 00000004450 15247115235 0013006 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity\Settings; use OCP\Activity\ISetting; use OCP\IL10N; class FileChanged implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'file_changed'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('A file or folder has been <strong>changed</strong> or <strong>renamed</strong>'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 1; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } Activity/Settings/FavoriteAction.php 0000604 00000004441 15247115235 0013572 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity\Settings; use OCP\Activity\ISetting; use OCP\IL10N; class FavoriteAction implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'favorite'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('A file has been added to or removed from your <strong>favorites</strong>'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 5; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } Activity/FavoriteProvider.php 0000604 00000013017 15247115235 0012346 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files\Activity; use OCP\Activity\IEvent; use OCP\Activity\IEventMerger; use OCP\Activity\IManager; use OCP\Activity\IProvider; use OCP\IL10N; use OCP\IURLGenerator; use OCP\L10N\IFactory; class FavoriteProvider implements IProvider { const SUBJECT_ADDED = 'added_favorite'; const SUBJECT_REMOVED = 'removed_favorite'; /** @var IFactory */ protected $languageFactory; /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** @var IManager */ protected $activityManager; /** @var IEventMerger */ protected $eventMerger; /** * @param IFactory $languageFactory * @param IURLGenerator $url * @param IManager $activityManager * @param IEventMerger $eventMerger */ public function __construct(IFactory $languageFactory, IURLGenerator $url, IManager $activityManager, IEventMerger $eventMerger) { $this->languageFactory = $languageFactory; $this->url = $url; $this->activityManager = $activityManager; $this->eventMerger = $eventMerger; } /** * @param string $language * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parse($language, IEvent $event, IEvent $previousEvent = null) { if ($event->getApp() !== 'files' || $event->getType() !== 'favorite') { throw new \InvalidArgumentException(); } $this->l = $this->languageFactory->get('files', $language); if ($this->activityManager->isFormattingFilteredObject()) { try { return $this->parseShortVersion($event); } catch (\InvalidArgumentException $e) { // Ignore and simply use the long version... } } return $this->parseLongVersion($event, $previousEvent); } /** * @param IEvent $event * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parseShortVersion(IEvent $event) { if ($event->getSubject() === self::SUBJECT_ADDED) { $event->setParsedSubject($this->l->t('Added to favorites')); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/starred.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/starred.svg'))); } } else if ($event->getSubject() === self::SUBJECT_REMOVED) { $event->setParsedSubject($this->l->t('Removed from favorites')); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/star.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/star.svg'))); } } else { throw new \InvalidArgumentException(); } return $event; } /** * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parseLongVersion(IEvent $event, IEvent $previousEvent = null) { if ($event->getSubject() === self::SUBJECT_ADDED) { $subject = $this->l->t('You added {file} to your favorites'); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/starred.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/starred.svg'))); } } else if ($event->getSubject() === self::SUBJECT_REMOVED) { $subject = $this->l->t('You removed {file} from your favorites'); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/star.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/star.svg'))); } } else { throw new \InvalidArgumentException(); } $this->setSubjects($event, $subject); $event = $this->eventMerger->mergeEvents('file', $event, $previousEvent); return $event; } /** * @param IEvent $event * @param string $subject */ protected function setSubjects(IEvent $event, $subject) { $subjectParams = $event->getSubjectParameters(); if (empty($subjectParams)) { // Try to fall back to the old way, but this does not work for emails. // But at least old activities still work. $subjectParams = [ 'id' => $event->getObjectId(), 'path' => $event->getObjectName(), ]; } $parameter = [ 'type' => 'file', 'id' => $subjectParams['id'], 'name' => basename($subjectParams['path']), 'path' => trim($subjectParams['path'], '/'), 'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $subjectParams['id']]), ]; $event->setParsedSubject(str_replace('{file}', $parameter['path'], $subject)) ->setRichSubject($subject, ['file' => $parameter]); } } Helper.php 0000604 00000017500 15247115235 0006500 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author brumsel <brumsel@losecatcher.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files; use OCP\Files\FileInfo; use OCP\ITagManager; /** * Helper class for manipulating file information */ class Helper { /** * @param string $dir * @return array * @throws \OCP\Files\NotFoundException */ public static function buildFileStorageStatistics($dir) { // information about storage capacities $storageInfo = \OC_Helper::getStorageInfo($dir); $l = \OC::$server->getL10N('files'); $maxUploadFileSize = \OCP\Util::maxUploadFilesize($dir, $storageInfo['free']); $maxHumanFileSize = \OCP\Util::humanFileSize($maxUploadFileSize); $maxHumanFileSize = $l->t('Upload (max. %s)', array($maxHumanFileSize)); return [ 'uploadMaxFilesize' => $maxUploadFileSize, 'maxHumanFilesize' => $maxHumanFileSize, 'freeSpace' => $storageInfo['free'], 'usedSpacePercent' => (int)$storageInfo['relative'], 'owner' => $storageInfo['owner'], 'ownerDisplayName' => $storageInfo['ownerDisplayName'], ]; } /** * Determine icon for a given file * * @param \OCP\Files\FileInfo $file file info * @return string icon URL */ public static function determineIcon($file) { if($file['type'] === 'dir') { $icon = \OC::$server->getMimeTypeDetector()->mimeTypeIcon('dir'); // TODO: move this part to the client side, using mountType if ($file->isShared()) { $icon = \OC::$server->getMimeTypeDetector()->mimeTypeIcon('dir-shared'); } elseif ($file->isMounted()) { $icon = \OC::$server->getMimeTypeDetector()->mimeTypeIcon('dir-external'); } }else{ $icon = \OC::$server->getMimeTypeDetector()->mimeTypeIcon($file->getMimetype()); } return substr($icon, 0, -3) . 'svg'; } /** * Comparator function to sort files alphabetically and have * the directories appear first * * @param \OCP\Files\FileInfo $a file * @param \OCP\Files\FileInfo $b file * @return int -1 if $a must come before $b, 1 otherwise */ public static function compareFileNames(FileInfo $a, FileInfo $b) { $aType = $a->getType(); $bType = $b->getType(); if ($aType === 'dir' and $bType !== 'dir') { return -1; } elseif ($aType !== 'dir' and $bType === 'dir') { return 1; } else { return \OCP\Util::naturalSortCompare($a->getName(), $b->getName()); } } /** * Comparator function to sort files by date * * @param \OCP\Files\FileInfo $a file * @param \OCP\Files\FileInfo $b file * @return int -1 if $a must come before $b, 1 otherwise */ public static function compareTimestamp(FileInfo $a, FileInfo $b) { $aTime = $a->getMTime(); $bTime = $b->getMTime(); return ($aTime < $bTime) ? -1 : 1; } /** * Comparator function to sort files by size * * @param \OCP\Files\FileInfo $a file * @param \OCP\Files\FileInfo $b file * @return int -1 if $a must come before $b, 1 otherwise */ public static function compareSize(FileInfo $a, FileInfo $b) { $aSize = $a->getSize(); $bSize = $b->getSize(); return ($aSize < $bSize) ? -1 : 1; } /** * Formats the file info to be returned as JSON to the client. * * @param \OCP\Files\FileInfo $i * @return array formatted file info */ public static function formatFileInfo(FileInfo $i) { $entry = array(); $entry['id'] = $i['fileid']; $entry['parentId'] = $i['parent']; $entry['mtime'] = $i['mtime'] * 1000; // only pick out the needed attributes $entry['name'] = $i->getName(); $entry['permissions'] = $i['permissions']; $entry['mimetype'] = $i['mimetype']; $entry['size'] = $i['size']; $entry['type'] = $i['type']; $entry['etag'] = $i['etag']; if (isset($i['tags'])) { $entry['tags'] = $i['tags']; } if (isset($i['displayname_owner'])) { $entry['shareOwner'] = $i['displayname_owner']; } if (isset($i['is_share_mount_point'])) { $entry['isShareMountPoint'] = $i['is_share_mount_point']; } $mountType = null; $mount = $i->getMountPoint(); $mountType = $mount->getMountType(); if ($mountType !== '') { if ($i->getInternalPath() === '') { $mountType .= '-root'; } $entry['mountType'] = $mountType; } if (isset($i['extraData'])) { $entry['extraData'] = $i['extraData']; } return $entry; } /** * Format file info for JSON * @param \OCP\Files\FileInfo[] $fileInfos file infos * @return array */ public static function formatFileInfos($fileInfos) { $files = array(); foreach ($fileInfos as $i) { $files[] = self::formatFileInfo($i); } return $files; } /** * Retrieves the contents of the given directory and * returns it as a sorted array of FileInfo. * * @param string $dir path to the directory * @param string $sortAttribute attribute to sort on * @param bool $sortDescending true for descending sort, false otherwise * @param string $mimetypeFilter limit returned content to this mimetype or mimepart * @return \OCP\Files\FileInfo[] files */ public static function getFiles($dir, $sortAttribute = 'name', $sortDescending = false, $mimetypeFilter = '') { $content = \OC\Files\Filesystem::getDirectoryContent($dir, $mimetypeFilter); return self::sortFiles($content, $sortAttribute, $sortDescending); } /** * Populate the result set with file tags * * @param array $fileList * @param string $fileIdentifier identifier attribute name for values in $fileList * @param ITagManager $tagManager * @return array file list populated with tags */ public static function populateTags(array $fileList, $fileIdentifier = 'fileid', ITagManager $tagManager) { $ids = []; foreach ($fileList as $fileData) { $ids[] = $fileData[$fileIdentifier]; } $tagger = $tagManager->load('files'); $tags = $tagger->getTagsForObjects($ids); if (!is_array($tags)) { throw new \UnexpectedValueException('$tags must be an array'); } // Set empty tag array foreach ($fileList as $key => $fileData) { $fileList[$key]['tags'] = []; } if (!empty($tags)) { foreach ($tags as $fileId => $fileTags) { foreach ($fileList as $key => $fileData) { if ($fileId !== $fileData[$fileIdentifier]) { continue; } $fileList[$key]['tags'] = $fileTags; } } } return $fileList; } /** * Sort the given file info array * * @param \OCP\Files\FileInfo[] $files files to sort * @param string $sortAttribute attribute to sort on * @param bool $sortDescending true for descending sort, false otherwise * @return \OCP\Files\FileInfo[] sorted files */ public static function sortFiles($files, $sortAttribute = 'name', $sortDescending = false) { $sortFunc = 'compareFileNames'; if ($sortAttribute === 'mtime') { $sortFunc = 'compareTimestamp'; } else if ($sortAttribute === 'size') { $sortFunc = 'compareSize'; } usort($files, array('\OCA\Files\Helper', $sortFunc)); if ($sortDescending) { $files = array_reverse($files); } return $files; } } AppInfo/Application.php 0000604 00000014764 15247115235 0011071 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\AppInfo; use OC\AppFramework\Utility\SimpleContainer; use OCA\DAV\CalDAV\Activity\Backend; use OCA\DAV\CalDAV\Activity\Provider\Event; use OCA\DAV\CalDAV\BirthdayService; use OCA\DAV\Capabilities; use OCA\DAV\CardDAV\ContactsManager; use OCA\DAV\CardDAV\PhotoCache; use OCA\DAV\CardDAV\SyncService; use OCA\DAV\HookManager; use \OCP\AppFramework\App; use OCP\Contacts\IManager; use OCP\IUser; use Symfony\Component\EventDispatcher\GenericEvent; class Application extends App { /** * Application constructor. */ public function __construct() { parent::__construct('dav'); $container = $this->getContainer(); $server = $container->getServer(); $container->registerService(PhotoCache::class, function(SimpleContainer $s) use ($server) { return new PhotoCache( $server->getAppDataDir('dav-photocache') ); }); /* * Register capabilities */ $container->registerCapability(Capabilities::class); } /** * @param IManager $contactsManager * @param string $userID */ public function setupContactsProvider(IManager $contactsManager, $userID) { /** @var ContactsManager $cm */ $cm = $this->getContainer()->query(ContactsManager::class); $urlGenerator = $this->getContainer()->getServer()->getURLGenerator(); $cm->setupContactsProvider($contactsManager, $userID, $urlGenerator); } public function registerHooks() { /** @var HookManager $hm */ $hm = $this->getContainer()->query(HookManager::class); $hm->setup(); $dispatcher = $this->getContainer()->getServer()->getEventDispatcher(); // first time login event setup $dispatcher->addListener(IUser::class . '::firstLogin', function ($event) use ($hm) { if ($event instanceof GenericEvent) { $hm->firstLogin($event->getSubject()); } }); // carddav/caldav sync event setup $listener = function($event) { if ($event instanceof GenericEvent) { /** @var BirthdayService $b */ $b = $this->getContainer()->query(BirthdayService::class); $b->onCardChanged( $event->getArgument('addressBookId'), $event->getArgument('cardUri'), $event->getArgument('cardData') ); } }; $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', $listener); $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $listener); $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function($event) { if ($event instanceof GenericEvent) { /** @var BirthdayService $b */ $b = $this->getContainer()->query(BirthdayService::class); $b->onCardDeleted( $event->getArgument('addressBookId'), $event->getArgument('cardUri') ); } }); $clearPhotoCache = function($event) { if ($event instanceof GenericEvent) { /** @var PhotoCache $p */ $p = $this->getContainer()->query(PhotoCache::class); $p->delete( $event->getArgument('addressBookId'), $event->getArgument('cardUri') ); } }; $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $clearPhotoCache); $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', $clearPhotoCache); $dispatcher->addListener('OC\AccountManager::userUpdated', function(GenericEvent $event) { $user = $event->getSubject(); $syncService = $this->getContainer()->query(SyncService::class); $syncService->updateUser($user); }); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createCalendar', function(GenericEvent $event) { /** @var Backend $backend */ $backend = $this->getContainer()->query(Backend::class); $backend->onCalendarAdd( $event->getArgument('calendarData') ); }); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateCalendar', function(GenericEvent $event) { /** @var Backend $backend */ $backend = $this->getContainer()->query(Backend::class); $backend->onCalendarUpdate( $event->getArgument('calendarData'), $event->getArgument('shares'), $event->getArgument('propertyMutations') ); }); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', function(GenericEvent $event) { /** @var Backend $backend */ $backend = $this->getContainer()->query(Backend::class); $backend->onCalendarDelete( $event->getArgument('calendarData'), $event->getArgument('shares') ); }); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateShares', function(GenericEvent $event) { /** @var Backend $backend */ $backend = $this->getContainer()->query(Backend::class); $backend->onCalendarUpdateShares( $event->getArgument('calendarData'), $event->getArgument('shares'), $event->getArgument('add'), $event->getArgument('remove') ); }); $listener = function(GenericEvent $event, $eventName) { /** @var Backend $backend */ $backend = $this->getContainer()->query(Backend::class); $subject = Event::SUBJECT_OBJECT_ADD; if ($eventName === '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject') { $subject = Event::SUBJECT_OBJECT_UPDATE; } else if ($eventName === '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject') { $subject = Event::SUBJECT_OBJECT_DELETE; } $backend->onTouchCalendarObject( $subject, $event->getArgument('calendarData'), $event->getArgument('shares'), $event->getArgument('objectData') ); }; $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', $listener); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', $listener); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', $listener); } public function getSyncService() { return $this->getContainer()->query(SyncService::class); } } Service/TagService.php 0000604 00000007123 15247115235 0010715 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\Service; use OC\Tags; use OCA\Files\Activity\FavoriteProvider; use OCP\Activity\IManager; use OCP\Files\Folder; use OCP\ITags; use OCP\IUser; use OCP\IUserSession; /** * Service class to manage tags on files. */ class TagService { /** @var IUserSession */ private $userSession; /** @var IManager */ private $activityManager; /** @var ITags */ private $tagger; /** @var Folder */ private $homeFolder; /** * @param IUserSession $userSession * @param IManager $activityManager * @param ITags $tagger * @param Folder $homeFolder */ public function __construct( IUserSession $userSession, IManager $activityManager, ITags $tagger, Folder $homeFolder ) { $this->userSession = $userSession; $this->activityManager = $activityManager; $this->tagger = $tagger; $this->homeFolder = $homeFolder; } /** * Updates the tags of the specified file path. * The passed tags are absolute, which means they will * replace the actual tag selection. * * @param string $path path * @param array $tags array of tags * @return array list of tags * @throws \OCP\Files\NotFoundException if the file does not exist */ public function updateFileTags($path, $tags) { $fileId = $this->homeFolder->get($path)->getId(); $currentTags = $this->tagger->getTagsForObjects(array($fileId)); if (!empty($currentTags)) { $currentTags = current($currentTags); } $newTags = array_diff($tags, $currentTags); foreach ($newTags as $tag) { if ($tag === Tags::TAG_FAVORITE) { $this->addActivity(true, $fileId, $path); } $this->tagger->tagAs($fileId, $tag); } $deletedTags = array_diff($currentTags, $tags); foreach ($deletedTags as $tag) { if ($tag === Tags::TAG_FAVORITE) { $this->addActivity(false, $fileId, $path); } $this->tagger->unTag($fileId, $tag); } // TODO: re-read from tagger to make sure the // list is up to date, in case of concurrent changes ? return $tags; } /** * @param bool $addToFavorite * @param int $fileId * @param string $path */ protected function addActivity($addToFavorite, $fileId, $path) { $user = $this->userSession->getUser(); if (!$user instanceof IUser) { return; } $event = $this->activityManager->generateEvent(); try { $event->setApp('files') ->setObject('files', $fileId, $path) ->setType('favorite') ->setAuthor($user->getUID()) ->setAffectedUser($user->getUID()) ->setTimestamp(time()) ->setSubject( $addToFavorite ? FavoriteProvider::SUBJECT_ADDED : FavoriteProvider::SUBJECT_REMOVED, ['id' => $fileId, 'path' => $path] ); $this->activityManager->publish($event); } catch (\InvalidArgumentException $e) { } catch (\BadMethodCallException $e) { } } } Command/DeleteOrphanedFiles.php 0000604 00000004646 15247115235 0012514 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\Command; use OCP\IDBConnection; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** * Delete all file entries that have no matching entries in the storage table. */ class DeleteOrphanedFiles extends Command { const CHUNK_SIZE = 200; /** * @var IDBConnection */ protected $connection; public function __construct(IDBConnection $connection) { $this->connection = $connection; parent::__construct(); } protected function configure() { $this ->setName('files:cleanup') ->setDescription('cleanup filecache'); } public function execute(InputInterface $input, OutputInterface $output) { $deletedEntries = 0; $query = $this->connection->getQueryBuilder(); $query->select('fc.fileid') ->from('filecache', 'fc') ->where($query->expr()->isNull('s.numeric_id')) ->leftJoin('fc', 'storages', 's', $query->expr()->eq('fc.storage', 's.numeric_id')) ->setMaxResults(self::CHUNK_SIZE); $deleteQuery = $this->connection->getQueryBuilder(); $deleteQuery->delete('filecache') ->where($deleteQuery->expr()->eq('fileid', $deleteQuery->createParameter('objectid'))); $deletedInLastChunk = self::CHUNK_SIZE; while ($deletedInLastChunk === self::CHUNK_SIZE) { $deletedInLastChunk = 0; $result = $query->execute(); while ($row = $result->fetch()) { $deletedInLastChunk++; $deletedEntries += $deleteQuery->setParameter('objectid', (int) $row['fileid']) ->execute(); } $result->closeCursor(); } $output->writeln("$deletedEntries orphaned file cache entries deleted"); } } Command/ScanAppData.php 0000604 00000017671 15247115235 0010767 0 ustar 00 <?php namespace OCA\Files\Command; use Doctrine\DBAL\Connection; use OC\Core\Command\Base; use OC\Core\Command\InterruptedException; use OC\ForbiddenException; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\StorageNotAvailableException; use OCP\IConfig; use OCP\IDBConnection; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Helper\Table; class ScanAppData extends Base { /** @var IRootFolder */ protected $root; /** @var IConfig */ protected $config; /** @var float */ protected $execTime = 0; /** @var int */ protected $foldersCounter = 0; /** @var int */ protected $filesCounter = 0; public function __construct(IRootFolder $rootFolder, IConfig $config) { parent::__construct(); $this->root = $rootFolder; $this->config = $config; } protected function configure() { parent::configure(); $this ->setName('files:scan-app-data') ->setDescription('rescan the AppData folder') ->addOption( 'quiet', 'q', InputOption::VALUE_NONE, 'suppress any output' ) ->addOption( 'verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'verbose the output' ); } public function checkScanWarning($fullPath, OutputInterface $output) { $normalizedPath = basename(\OC\Files\Filesystem::normalizePath($fullPath)); $path = basename($fullPath); if ($normalizedPath !== $path) { $output->writeln("\t<error>Entry \"" . $fullPath . '" will not be accessible due to incompatible encoding</error>'); } } protected function scanFiles($verbose, OutputInterface $output) { try { $appData = $this->getAppDataFolder(); } catch (NotFoundException $e) { $output->writeln('NoAppData folder found'); return; } $connection = $this->reconnectToDatabase($output); $scanner = new \OC\Files\Utils\Scanner(null, $connection, \OC::$server->getLogger()); # check on each file/folder if there was a user interrupt (ctrl-c) and throw an exception # printout and count if ($verbose) { $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) { $output->writeln("\tFile <info>$path</info>"); $this->filesCounter += 1; if ($this->hasBeenInterrupted()) { throw new InterruptedException(); } }); $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) { $output->writeln("\tFolder <info>$path</info>"); $this->foldersCounter += 1; if ($this->hasBeenInterrupted()) { throw new InterruptedException(); } }); $scanner->listen('\OC\Files\Utils\Scanner', 'StorageNotAvailable', function (StorageNotAvailableException $e) use ($output) { $output->writeln("Error while scanning, storage not available (" . $e->getMessage() . ")"); }); # count only } else { $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function () use ($output) { $this->filesCounter += 1; if ($this->hasBeenInterrupted()) { throw new InterruptedException(); } }); $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function () use ($output) { $this->foldersCounter += 1; if ($this->hasBeenInterrupted()) { throw new InterruptedException(); } }); } $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function($path) use ($output) { $this->checkScanWarning($path, $output); }); $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function($path) use ($output) { $this->checkScanWarning($path, $output); }); try { $scanner->scan($appData->getPath()); } catch (ForbiddenException $e) { $output->writeln("<error>Storage not writable</error>"); $output->writeln("Make sure you're running the scan command only as the user the web server runs as"); } catch (InterruptedException $e) { # exit the function if ctrl-c has been pressed $output->writeln('Interrupted by user'); } catch (NotFoundException $e) { $output->writeln('<error>Path not found: ' . $e->getMessage() . '</error>'); } catch (\Exception $e) { $output->writeln('<error>Exception during scan: ' . $e->getMessage() . '</error>'); $output->writeln('<error>' . $e->getTraceAsString() . '</error>'); } } protected function execute(InputInterface $input, OutputInterface $output) { # no messaging level option means: no full printout but statistics # $quiet means no print at all # $verbose means full printout including statistics # -q -v full stat # 0 0 no yes # 0 1 yes yes # 1 -- no no (quiet overrules verbose) $verbose = $input->getOption('verbose'); $quiet = $input->getOption('quiet'); # restrict the verbosity level to VERBOSITY_VERBOSE if ($output->getVerbosity() > OutputInterface::VERBOSITY_VERBOSE) { $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); } if ($quiet) { $verbose = false; } $output->writeln("\nScanning AppData for files"); $this->initTools(); $this->scanFiles($verbose, $output); # stat: printout statistics if $quiet was not set if (!$quiet) { $this->presentStats($output); } } /** * Initialises some useful tools for the Command */ protected function initTools() { // Start the timer $this->execTime = -microtime(true); // Convert PHP errors to exceptions set_error_handler([$this, 'exceptionErrorHandler'], E_ALL); } /** * Processes PHP errors as exceptions in order to be able to keep track of problems * * @see https://secure.php.net/manual/en/function.set-error-handler.php * * @param int $severity the level of the error raised * @param string $message * @param string $file the filename that the error was raised in * @param int $line the line number the error was raised * * @throws \ErrorException */ public function exceptionErrorHandler($severity, $message, $file, $line) { if (!(error_reporting() & $severity)) { // This error code is not included in error_reporting return; } throw new \ErrorException($message, 0, $severity, $file, $line); } /** * @param OutputInterface $output */ protected function presentStats(OutputInterface $output) { // Stop the timer $this->execTime += microtime(true); $output->writeln(""); $headers = [ 'Folders', 'Files', 'Elapsed time' ]; $this->showSummary($headers, null, $output); } /** * Shows a summary of operations * * @param string[] $headers * @param string[] $rows * @param OutputInterface $output */ protected function showSummary($headers, $rows, OutputInterface $output) { $niceDate = $this->formatExecTime(); if (!$rows) { $rows = [ $this->foldersCounter, $this->filesCounter, $niceDate, ]; } $table = new Table($output); $table ->setHeaders($headers) ->setRows([$rows]); $table->render(); } /** * Formats microtime into a human readable format * * @return string */ protected function formatExecTime() { list($secs, $tens) = explode('.', sprintf("%.1f", ($this->execTime))); # if you want to have microseconds add this: . '.' . $tens; return date('H:i:s', $secs); } /** * @return \OCP\IDBConnection */ protected function reconnectToDatabase(OutputInterface $output) { /** @var Connection | IDBConnection $connection*/ $connection = \OC::$server->getDatabaseConnection(); try { $connection->close(); } catch (\Exception $ex) { $output->writeln("<info>Error while disconnecting from database: {$ex->getMessage()}</info>"); } while (!$connection->isConnected()) { try { $connection->connect(); } catch (\Exception $ex) { $output->writeln("<info>Error while re-connecting to database: {$ex->getMessage()}</info>"); sleep(60); } } return $connection; } /** * @return \OCP\Files\Folder * @throws NotFoundException */ private function getAppDataFolder() { $instanceId = $this->config->getSystemValue('instanceid', null); if ($instanceId === null) { throw new NotFoundException(); } return $this->root->get('appdata_'.$instanceId); } } Command/Scan.php 0000604 00000025130 15247115235 0007521 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author martin.mattel@diemattels.at <martin.mattel@diemattels.at> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\Command; use Doctrine\DBAL\Connection; use OC\Core\Command\Base; use OC\Core\Command\InterruptedException; use OC\ForbiddenException; use OCP\Files\NotFoundException; use OCP\Files\StorageNotAvailableException; use OCP\IDBConnection; use OCP\IUserManager; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Helper\Table; class Scan extends Base { /** @var IUserManager $userManager */ private $userManager; /** @var float */ protected $execTime = 0; /** @var int */ protected $foldersCounter = 0; /** @var int */ protected $filesCounter = 0; public function __construct(IUserManager $userManager) { $this->userManager = $userManager; parent::__construct(); } protected function configure() { parent::configure(); $this ->setName('files:scan') ->setDescription('rescan filesystem') ->addArgument( 'user_id', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'will rescan all files of the given user(s)' ) ->addOption( 'path', 'p', InputArgument::OPTIONAL, 'limit rescan to this path, eg. --path="/alice/files/Music", the user_id is determined by the path and the user_id parameter and --all are ignored' ) ->addOption( 'quiet', 'q', InputOption::VALUE_NONE, 'suppress any output' ) ->addOption( 'verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'verbose the output' ) ->addOption( 'all', null, InputOption::VALUE_NONE, 'will rescan all files of all known users' )->addOption( 'unscanned', null, InputOption::VALUE_NONE, 'only scan files which are marked as not fully scanned' ); } public function checkScanWarning($fullPath, OutputInterface $output) { $normalizedPath = basename(\OC\Files\Filesystem::normalizePath($fullPath)); $path = basename($fullPath); if ($normalizedPath !== $path) { $output->writeln("\t<error>Entry \"" . $fullPath . '" will not be accessible due to incompatible encoding</error>'); } } protected function scanFiles($user, $path, $verbose, OutputInterface $output, $backgroundScan = false) { $connection = $this->reconnectToDatabase($output); $scanner = new \OC\Files\Utils\Scanner($user, $connection, \OC::$server->getLogger()); # check on each file/folder if there was a user interrupt (ctrl-c) and throw an exception # printout and count if ($verbose) { $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) { $output->writeln("\tFile <info>$path</info>"); $this->filesCounter += 1; if ($this->hasBeenInterrupted()) { throw new InterruptedException(); } }); $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) { $output->writeln("\tFolder <info>$path</info>"); $this->foldersCounter += 1; if ($this->hasBeenInterrupted()) { throw new InterruptedException(); } }); $scanner->listen('\OC\Files\Utils\Scanner', 'StorageNotAvailable', function (StorageNotAvailableException $e) use ($output) { $output->writeln("Error while scanning, storage not available (" . $e->getMessage() . ")"); }); # count only } else { $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function () use ($output) { $this->filesCounter += 1; if ($this->hasBeenInterrupted()) { throw new InterruptedException(); } }); $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function () use ($output) { $this->foldersCounter += 1; if ($this->hasBeenInterrupted()) { throw new InterruptedException(); } }); } $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) { $this->checkScanWarning($path, $output); }); $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) { $this->checkScanWarning($path, $output); }); try { if ($backgroundScan) { $scanner->backgroundScan($path); } else { $scanner->scan($path); } } catch (ForbiddenException $e) { $output->writeln("<error>Home storage for user $user not writable</error>"); $output->writeln("Make sure you're running the scan command only as the user the web server runs as"); } catch (InterruptedException $e) { # exit the function if ctrl-c has been pressed $output->writeln('Interrupted by user'); } catch (NotFoundException $e) { $output->writeln('<error>Path not found: ' . $e->getMessage() . '</error>'); } catch (\Exception $e) { $output->writeln('<error>Exception during scan: ' . $e->getMessage() . '</error>'); $output->writeln('<error>' . $e->getTraceAsString() . '</error>'); } } protected function execute(InputInterface $input, OutputInterface $output) { $inputPath = $input->getOption('path'); if ($inputPath) { $inputPath = '/' . trim($inputPath, '/'); list (, $user,) = explode('/', $inputPath, 3); $users = array($user); } else if ($input->getOption('all')) { $users = $this->userManager->search(''); } else { $users = $input->getArgument('user_id'); } # no messaging level option means: no full printout but statistics # $quiet means no print at all # $verbose means full printout including statistics # -q -v full stat # 0 0 no yes # 0 1 yes yes # 1 -- no no (quiet overrules verbose) $verbose = $input->getOption('verbose'); $quiet = $input->getOption('quiet'); # restrict the verbosity level to VERBOSITY_VERBOSE if ($output->getVerbosity() > OutputInterface::VERBOSITY_VERBOSE) { $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); } if ($quiet) { $verbose = false; } # check quantity of users to be process and show it on the command line $users_total = count($users); if ($users_total === 0) { $output->writeln("<error>Please specify the user id to scan, \"--all\" to scan for all users or \"--path=...\"</error>"); return; } else { if ($users_total > 1) { $output->writeln("\nScanning files for $users_total users"); } } $this->initTools(); $user_count = 0; foreach ($users as $user) { if (is_object($user)) { $user = $user->getUID(); } $path = $inputPath ? $inputPath : '/' . $user; $user_count += 1; if ($this->userManager->userExists($user)) { # add an extra line when verbose is set to optical separate users if ($verbose) { $output->writeln(""); } $output->writeln("Starting scan for user $user_count out of $users_total ($user)"); # full: printout data if $verbose was set $this->scanFiles($user, $path, $verbose, $output, $input->getOption('unscanned')); } else { $output->writeln("<error>Unknown user $user_count $user</error>"); } # check on each user if there was a user interrupt (ctrl-c) and exit foreach if ($this->hasBeenInterrupted()) { break; } } # stat: printout statistics if $quiet was not set if (!$quiet) { $this->presentStats($output); } } /** * Initialises some useful tools for the Command */ protected function initTools() { // Start the timer $this->execTime = -microtime(true); // Convert PHP errors to exceptions set_error_handler([$this, 'exceptionErrorHandler'], E_ALL); } /** * Processes PHP errors as exceptions in order to be able to keep track of problems * * @see https://secure.php.net/manual/en/function.set-error-handler.php * * @param int $severity the level of the error raised * @param string $message * @param string $file the filename that the error was raised in * @param int $line the line number the error was raised * * @throws \ErrorException */ public function exceptionErrorHandler($severity, $message, $file, $line) { if (!(error_reporting() & $severity)) { // This error code is not included in error_reporting return; } throw new \ErrorException($message, 0, $severity, $file, $line); } /** * @param OutputInterface $output */ protected function presentStats(OutputInterface $output) { // Stop the timer $this->execTime += microtime(true); $output->writeln(""); $headers = [ 'Folders', 'Files', 'Elapsed time' ]; $this->showSummary($headers, null, $output); } /** * Shows a summary of operations * * @param string[] $headers * @param string[] $rows * @param OutputInterface $output */ protected function showSummary($headers, $rows, OutputInterface $output) { $niceDate = $this->formatExecTime(); if (!$rows) { $rows = [ $this->foldersCounter, $this->filesCounter, $niceDate, ]; } $table = new Table($output); $table ->setHeaders($headers) ->setRows([$rows]); $table->render(); } /** * Formats microtime into a human readable format * * @return string */ protected function formatExecTime() { list($secs, $tens) = explode('.', sprintf("%.1f", ($this->execTime))); # if you want to have microseconds add this: . '.' . $tens; return date('H:i:s', $secs); } /** * @return \OCP\IDBConnection */ protected function reconnectToDatabase(OutputInterface $output) { /** @var Connection | IDBConnection $connection */ $connection = \OC::$server->getDatabaseConnection(); try { $connection->close(); } catch (\Exception $ex) { $output->writeln("<info>Error while disconnecting from database: {$ex->getMessage()}</info>"); } while (!$connection->isConnected()) { try { $connection->connect(); } catch (\Exception $ex) { $output->writeln("<info>Error while re-connecting to database: {$ex->getMessage()}</info>"); sleep(60); } } return $connection; } } Command/TransferOwnership.php 0000604 00000021447 15247115235 0012327 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Carla Schroder <carla@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files\Command; use OC\Files\Filesystem; use OC\Files\View; use OCP\Files\FileInfo; use OCP\Files\IHomeStorage; use OCP\Files\Mount\IMountManager; use OCP\IUser; use OCP\IUserManager; use OCP\Share\IManager; use OCP\Share\IShare; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class TransferOwnership extends Command { /** @var IUserManager $userManager */ private $userManager; /** @var IManager */ private $shareManager; /** @var IMountManager */ private $mountManager; /** @var FileInfo[] */ private $allFiles = []; /** @var FileInfo[] */ private $encryptedFiles = []; /** @var IShare[] */ private $shares = []; /** @var string */ private $sourceUser; /** @var string */ private $destinationUser; /** @var string */ private $sourcePath; /** @var string */ private $finalTarget; public function __construct(IUserManager $userManager, IManager $shareManager, IMountManager $mountManager) { $this->userManager = $userManager; $this->shareManager = $shareManager; $this->mountManager = $mountManager; parent::__construct(); } protected function configure() { $this ->setName('files:transfer-ownership') ->setDescription('All files and folders are moved to another user - shares are moved as well.') ->addArgument( 'source-user', InputArgument::REQUIRED, 'owner of files which shall be moved' ) ->addArgument( 'destination-user', InputArgument::REQUIRED, 'user who will be the new owner of the files' ) ->addOption( 'path', null, InputOption::VALUE_REQUIRED, 'selectively provide the path to transfer. For example --path="folder_name"', '' ); } protected function execute(InputInterface $input, OutputInterface $output) { $sourceUserObject = $this->userManager->get($input->getArgument('source-user')); $destinationUserObject = $this->userManager->get($input->getArgument('destination-user')); if (!$sourceUserObject instanceof IUser) { $output->writeln("<error>Unknown source user $this->sourceUser</error>"); return 1; } if (!$destinationUserObject instanceof IUser) { $output->writeln("<error>Unknown destination user $this->destinationUser</error>"); return 1; } $this->sourceUser = $sourceUserObject->getUID(); $this->destinationUser = $destinationUserObject->getUID(); $sourcePathOption = ltrim($input->getOption('path'), '/'); $this->sourcePath = rtrim($this->sourceUser . '/files/' . $sourcePathOption, '/'); // target user has to be ready if (!\OC::$server->getEncryptionManager()->isReadyForUser($this->destinationUser)) { $output->writeln("<error>The target user is not ready to accept files. The user has at least to be logged in once.</error>"); return 2; } $date = date('Y-m-d H-i-s'); $this->finalTarget = "$this->destinationUser/files/transferred from $this->sourceUser on $date"; // setup filesystem Filesystem::initMountPoints($this->sourceUser); Filesystem::initMountPoints($this->destinationUser); $view = new View(); if (!$view->is_dir($this->sourcePath)) { $output->writeln("<error>Unknown path provided: $sourcePathOption</error>"); return 1; } // analyse source folder $this->analyse($output); // collect all the shares $this->collectUsersShares($output); // transfer the files $this->transfer($output); // restore the shares $this->restoreShares($output); } private function walkFiles(View $view, $path, \Closure $callBack) { foreach ($view->getDirectoryContent($path) as $fileInfo) { if (!$callBack($fileInfo)) { return; } if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) { $this->walkFiles($view, $fileInfo->getPath(), $callBack); } } } /** * @param OutputInterface $output * @throws \Exception */ protected function analyse(OutputInterface $output) { $view = new View(); $output->writeln("Analysing files of $this->sourceUser ..."); $progress = new ProgressBar($output); $progress->start(); $self = $this; $this->walkFiles($view, $this->sourcePath, function (FileInfo $fileInfo) use ($progress, $self) { if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) { // only analyze into folders from main storage, if (!$fileInfo->getStorage()->instanceOfStorage(IHomeStorage::class)) { return false; } return true; } $progress->advance(); $this->allFiles[] = $fileInfo; if ($fileInfo->isEncrypted()) { $this->encryptedFiles[] = $fileInfo; } return true; }); $progress->finish(); $output->writeln(''); // no file is allowed to be encrypted if (!empty($this->encryptedFiles)) { $output->writeln("<error>Some files are encrypted - please decrypt them first</error>"); foreach($this->encryptedFiles as $encryptedFile) { /** @var FileInfo $encryptedFile */ $output->writeln(" " . $encryptedFile->getPath()); } throw new \Exception('Execution terminated.'); } } /** * @param OutputInterface $output */ private function collectUsersShares(OutputInterface $output) { $output->writeln("Collecting all share information for files and folder of $this->sourceUser ..."); $progress = new ProgressBar($output, count($this->shares)); foreach([\OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE] as $shareType) { $offset = 0; while (true) { $sharePage = $this->shareManager->getSharesBy($this->sourceUser, $shareType, null, true, 50, $offset); $progress->advance(count($sharePage)); if (empty($sharePage)) { break; } $this->shares = array_merge($this->shares, $sharePage); $offset += 50; } } $progress->finish(); $output->writeln(''); } /** * @param OutputInterface $output */ protected function transfer(OutputInterface $output) { $view = new View(); $output->writeln("Transferring files to $this->finalTarget ..."); // This change will help user to transfer the folder specified using --path option. // Else only the content inside folder is transferred which is not correct. if($this->sourcePath !== "$this->sourceUser/files") { $view->mkdir($this->finalTarget); $this->finalTarget = $this->finalTarget . '/' . basename($this->sourcePath); } $view->rename($this->sourcePath, $this->finalTarget); if (!is_dir("$this->sourceUser/files")) { // because the files folder is moved away we need to recreate it $view->mkdir("$this->sourceUser/files"); } } /** * @param OutputInterface $output */ private function restoreShares(OutputInterface $output) { $output->writeln("Restoring shares ..."); $progress = new ProgressBar($output, count($this->shares)); foreach($this->shares as $share) { try { if ($share->getSharedWith() === $this->destinationUser) { // Unmount the shares before deleting, so we don't try to get the storage later on. $shareMountPoint = $this->mountManager->find('/' . $this->destinationUser . '/files' . $share->getTarget()); if ($shareMountPoint) { $this->mountManager->removeMount($shareMountPoint->getMountPoint()); } $this->shareManager->deleteShare($share); } else { if ($share->getShareOwner() === $this->sourceUser) { $share->setShareOwner($this->destinationUser); } if ($share->getSharedBy() === $this->sourceUser) { $share->setSharedBy($this->destinationUser); } $this->shareManager->updateShare($share); } } catch (\OCP\Files\NotFoundException $e) { $output->writeln('<error>Share with id ' . $share->getId() . ' points at deleted file, skipping</error>'); } catch (\Exception $e) { $output->writeln('<error>Could not restore share with id ' . $share->getId() . ':' . $e->getTraceAsString() . '</error>'); } $progress->advance(); } $progress->finish(); $output->writeln(''); } } Utility/EventSource.php 0000604 00000004576 15247115507 0011201 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Bart Visscher <bartv@thisnet.nl> * @author Felix Moeller <mail@felixmoeller.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Utility; /** * Class EventSource * * Wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) * * This version is tailored for the Gallery app, do not use elsewhere! * @link https://github.com/owncloud/core/blob/master/lib/private/eventsource.php * * @todo Replace with a library * * @package OCA\Gallery\Controller */ class EventSource implements \OCP\IEventSource { /** * @var bool */ private $started = false; protected function init() { if ($this->started) { return; } $this->started = true; // prevent php output buffering, caching and nginx buffering while (ob_get_level()) { ob_end_clean(); } header('Cache-Control: no-cache'); header('X-Accel-Buffering: no'); header("Content-Type: text/event-stream"); flush(); } /** * Sends a message to the client * * If only one parameter is given, a typeless message will be sent with that parameter as data * * @param string $type * @param mixed $data * * @throws \BadMethodCallException */ public function send($type, $data = null) { $this->validateMessage($type, $data); $this->init(); if (is_null($data)) { $data = $type; $type = null; } if (!empty($type)) { echo 'event: ' . $type . PHP_EOL; } echo 'data: ' . json_encode($data) . PHP_EOL; echo PHP_EOL; flush(); } /** * Closes the connection of the event source * * It's best to let the client close the stream */ public function close() { $this->send( '__internal__', 'close' ); } /** * Makes sure we have a message we can use * * @param string $type * @param mixed $data */ private function validateMessage($type, $data) { if ($data && !preg_match('/^[A-Za-z0-9_]+$/', $type)) { throw new \BadMethodCallException('Type needs to be alphanumeric (' . $type . ')'); } } } Http/ImageResponse.php 0000604 00000002442 15247115507 0010742 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Http; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http; /** * A renderer for images * * @package OCA\Gallery\Http */ class ImageResponse extends Response { /** * @var \OC_Image|string */ private $preview; /** * Constructor * * @param array $image image meta data * @param int $statusCode the HTTP status code, defaults to 200 */ public function __construct(array $image, $statusCode = Http::STATUS_OK) { $name = $image['name']; $this->preview = $image['preview']; $this->setStatus($statusCode); $this->addHeader('Content-type', $image['mimetype'] . '; charset=utf-8'); $this->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($name) . '; filename="' . rawurlencode($name) . '"' ); } /** * Returns the rendered image * * @return string the file */ public function render() { if ($this->preview instanceof \OC_Image) { // Uses imagepng() to output the image return $this->preview->data(); } else { return $this->preview; } } } Environment/NotFoundEnvException.php 0000604 00000000623 15247115507 0013651 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Environment; /** * Thrown when the Environment cannot find or access a node */ class NotFoundEnvException extends EnvironmentException {} Environment/Environment.php 0000604 00000022513 15247115507 0012073 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * @author Authors of \OCA\Files_Sharing\Helper * * @copyright Olivier Paroz 2017 * @copyright Authors of \OCA\Files_Sharing\Helper 2014-2016 */ namespace OCA\Gallery\Environment; use OCP\IUserManager; use OCP\Share; use OCP\Share\IShare; use OCP\ILogger; use OCP\Files\IRootFolder; use OCP\Files\Folder; use OCP\Files\Node; use OCP\Files\File; use OCP\Files\NotFoundException; /** * Builds the environment so that the services have access to the files and folders' owner * * @package OCA\Gallery\Environment */ class Environment { /** * @var string */ private $appName; /** * The userId of the logged-in user or the person sharing a folder publicly * * @var string */ private $userId; /** * The userFolder of the logged-in user or the ORIGINAL owner of the files which are shared * publicly * * A share needs to be tracked back to its original owner in order to be able to access the * resource * * @var Folder|null */ private $userFolder; /** * @var IUserManager */ private $userManager; /** * @var int */ private $sharedNodeId; /** * @var File|Folder */ private $sharedNode; /** * @var IRootFolder */ private $rootFolder; /** * @var ILogger */ private $logger; /** * The path to the userFolder for users with accounts: /userId/files * * For public folders, it's the path from the shared folder to the root folder in the original * owner's filesystem: /userId/files/parent_folder/shared_folder * * @var string */ private $fromRootToFolder; /** * The name of the shared folder * * @var string */ private $folderName; /** * @var string|null */ private $sharePassword; /*** * Constructor * * @param string $appName * @param string|null $userId * @param Folder|null $userFolder * @param IUserManager $userManager * @param IRootFolder $rootFolder * @param ILogger $logger */ public function __construct( $appName, $userId, $userFolder, IUserManager $userManager, IRootFolder $rootFolder, ILogger $logger ) { $this->appName = $appName; $this->userId = $userId; $this->userFolder = $userFolder; $this->userManager = $userManager; $this->rootFolder = $rootFolder; $this->logger = $logger; } /** * Creates the environment based on the share the token links to * * @param IShare $share */ public function setTokenBasedEnv($share) { $origShareOwnerId = $share->getShareOwner(); $this->userFolder = $this->rootFolder->getUserFolder($origShareOwnerId); $this->sharedNodeId = $share->getNodeId(); $this->sharedNode = $share->getNode(); $this->fromRootToFolder = $this->buildFromRootToFolder($this->sharedNodeId); $this->folderName = $share->getTarget(); $this->userId = $origShareOwnerId; $this->sharePassword = $share->getPassword(); } /** * Creates the environment for a logged-in user * * userId and userFolder are already known, we define fromRootToFolder * so that the services can use one method to have access to resources * without having to know whether they're private or public */ public function setStandardEnv() { $this->fromRootToFolder = $this->userFolder->getPath() . '/'; } /** * Returns true if the environment has been setup using a token * * @return bool */ public function isTokenBasedEnv() { return !empty($this->sharedNodeId); } /** * Returns the Node based on a path starting from the virtual root * * @param string $subPath * * @return File|Folder */ public function getNodeFromVirtualRoot($subPath) { $relativePath = $this->getRelativePath($this->fromRootToFolder); $path = $relativePath . '/' . $subPath; $node = $this->getNodeFromUserFolder($path); return $this->getResourceFromId($node->getId()); } /** * Returns the Node based on a path starting from the files' owner user folder * * When logged in, this is the current user's user folder * When visiting a link, this is the sharer's user folder * * @param string $path * * @return File|Folder * * @throws NotFoundEnvException */ public function getNodeFromUserFolder($path) { $folder = $this->userFolder; if ($folder === null) { throw new NotFoundEnvException("Could not access the user's folder"); } else { try { $node = $folder->get($path); } catch (NotFoundException $exception) { $message = 'Could not find anything at: ' . $exception->getMessage(); throw new NotFoundEnvException($message); } } return $node; } /** * Returns the resource identified by the given ID * * @param int $resourceId * * @return Node * * @throws NotFoundEnvException */ public function getResourceFromId($resourceId) { if ($this->isTokenBasedEnv()) { if ($this->sharedNode->getType() === 'dir') { $resource = $this->getResourceFromFolderAndId($this->sharedNode, $resourceId); } else { $resource = $this->sharedNode; } } else { $resource = $this->getResourceFromFolderAndId($this->userFolder, $resourceId); } return $resource; } /** * Returns the shared node * * @return File|Folder */ public function getSharedNode() { return $this->getResourceFromId($this->sharedNodeId); } /** * Returns the virtual root where the user lands after logging in or when following a link * * @return Folder * @throws NotFoundEnvException */ public function getVirtualRootFolder() { $rootFolder = $this->userFolder; if ($this->isTokenBasedEnv()) { $node = $this->getSharedNode(); $nodeType = $node->getType(); if ($nodeType === 'dir') { $rootFolder = $node; } else { throw new NotFoundEnvException($node->getPath() . ' is not a folder'); } } return $rootFolder; } /** * Returns the userId of the currently logged-in user or the sharer * * @return string */ public function getUserId() { return $this->userId; } /** * Returns the name of the user sharing files publicly * * @return string * @throws NotFoundEnvException */ public function getDisplayName() { $user = null; $userId = $this->userId; if (isset($userId)) { $user = $this->userManager->get($userId); } if ($user === null) { throw new NotFoundEnvException('Could not find user'); } return $user->getDisplayName(); } /** * Returns the name of shared folder * * @return string */ public function getSharedFolderName() { return trim($this->folderName, '//'); } /** * Returns the password for the share, if there is one * * @return string|null */ public function getSharePassword() { return $this->sharePassword; } /** * Returns the path which goes from the file, up to the user folder, based on a node: * parent_folder/current_folder/my_file * * This is used for the preview system, which needs a full path * * getPath() on the file produces a path like: * '/userId/files/my_folder/my_sub_folder/my_file' * * So we substract the path to the folder, giving us a relative path * 'my_folder/my_sub_folder/my_file' * * @param Node $file * * @return string */ public function getPathFromUserFolder($file) { $path = $file->getPath(); return $this->getRelativePath($path); } /** * Returns the path which goes from the file, up to the root folder of the Gallery: * current_folder/my_file * * That root folder changes when folders are shared publicly * * @param File|Folder|Node $node * * @return string */ public function getPathFromVirtualRoot($node) { $path = $node->getPath(); $nodeType = $node->getType(); // Needed because fromRootToFolder always ends with a slash if ($nodeType === 'dir') { $path .= '/'; } $path = str_replace($this->fromRootToFolder, '', $path); $path = rtrim($path, '/'); return $path; } /** * Returns the resource found in a specific folder and identified by the given ID * * @param Folder $folder * @param int $resourceId * * @return Node * @throws NotFoundEnvException */ private function getResourceFromFolderAndId($folder, $resourceId) { $resourcesArray = $folder->getById($resourceId); if (!isset($resourcesArray[0])) { throw new NotFoundEnvException('Could not locate node linked to ID: ' . $resourceId); } return $resourcesArray[0]; } /** * Returns the path from the shared folder to the root folder in the original * owner's filesystem: /userId/files/parent_folder/shared_folder * * This cannot be calculated with paths and IDs, the share's file source is required * * @param string $fileSource * * @return string */ private function buildFromRootToFolder($fileSource) { $resource = $this->getResourceFromId($fileSource); $fromRootToFolder = $resource->getPath() . '/'; return $fromRootToFolder; } /** * Returns the path which goes from the file, up to the user folder, based on a path: * parent_folder/current_folder/my_file * * getPath() on the file produces a path like: * '/userId/files/my_folder/my_sub_folder/my_file' * * So we substract the path to the user folder, giving us a relative path * 'my_folder/my_sub_folder' * * @param string $fullPath * * @return string */ private function getRelativePath($fullPath) { $folderPath = $this->userFolder->getPath() . '/'; $origShareRelPath = str_replace($folderPath, '', $fullPath); return $origShareRelPath; } } Environment/EnvironmentException.php 0000604 00000001160 15247115507 0013745 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Environment; use OCP\Util; /** * Thrown when the Environment runs into a problem */ class EnvironmentException extends \Exception { /** * Constructor * * @param string $msg the message contained in the exception */ public function __construct($msg) { Util::writeLog('gallery', 'Exception' . $msg, Util::ERROR); parent::__construct($msg); } } Middleware/EnvCheckMiddleware.php 0000604 00000017374 15247115507 0013035 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Authors of \OCA\Files_Sharing\Helper * * @copyright Olivier Paroz 2017 * @copyright Bernhard Posselt 2017 * @copyright Authors of \OCA\Files_Sharing\Helper 2017 */ namespace OCA\Gallery\Middleware; use OCP\IRequest; use OCP\IURLGenerator; use OCP\ISession; use OCP\ILogger; use OCP\Share; use OCP\Share\IShare; use OCP\Share\Exceptions\ShareNotFound; use OCP\Security\IHasher; use OCP\AppFramework\Http; use OCP\AppFramework\Utility\IControllerMethodReflector; use OCA\Gallery\Environment\Environment; use OCP\Share\IManager; /** * Checks that we have a valid token linked to a valid resource and that the * user is authorised to access it * * Once all checks have been passed, the environment is ready to use * * @package OCA\Gallery\Middleware */ class EnvCheckMiddleware extends CheckMiddleware { /** @var IHasher */ private $hasher; /** @var ISession */ private $session; /** @var Environment */ private $environment; /** @var IControllerMethodReflector */ protected $reflector; /** @var IManager */ protected $shareManager; /*** * Constructor * * @param string $appName * @param IRequest $request * @param IHasher $hasher * @param ISession $session * @param Environment $environment * @param IControllerMethodReflector $reflector * @param IURLGenerator $urlGenerator * @param ILogger $logger * @param IManager $shareManager */ public function __construct( $appName, IRequest $request, IHasher $hasher, ISession $session, Environment $environment, IControllerMethodReflector $reflector, IURLGenerator $urlGenerator, IManager $shareManager, ILogger $logger ) { parent::__construct( $appName, $request, $urlGenerator, $logger ); $this->hasher = $hasher; $this->session = $session; $this->environment = $environment; $this->reflector = $reflector; $this->shareManager = $shareManager; } /** * Checks that we have a valid token linked to a valid resource and that the * user is authorised to access it * * Inspects the controller method annotations and if PublicPage is found * it checks that we have a token and an optional password giving access to a valid resource. * Once that's done, the environment is setup so that our services can find the resources they * need. * * The checks are not performed on "guest" pages and the environment is not setup. Typical * guest pages are anonymous error ages * * @inheritDoc */ public function beforeController($controller, $methodName) { if ($this->reflector->hasAnnotation('Guest')) { return; } $isPublicPage = $this->reflector->hasAnnotation('PublicPage'); if ($isPublicPage) { $this->validateAndSetTokenBasedEnv(); } else { $this->environment->setStandardEnv(); } } /** * Checks that we have a token and an optional password giving access to a * valid resource. Sets the token based environment after that * * @throws CheckException */ private function validateAndSetTokenBasedEnv() { $token = $this->request->getParam('token'); if (!$token) { throw new CheckException( "Can't access a public resource without a token", Http::STATUS_NOT_FOUND ); } else { $share = $this->getShare($token); $password = $this->request->getParam('password'); // Let's see if the user needs to provide a password $this->checkAuthorisation($share, $password); $this->environment->setTokenBasedEnv($share); } } /** * Validates a token to make sure its linked to a valid resource * * Uses Share 2.0 * * @fixme setIncognitoMode in 8.1 https://github.com/owncloud/core/pull/12912 * * @param string $token * * @throws CheckException * @return IShare */ private function getShare($token) { // Allows a logged in user to access public links \OC_User::setIncognitoMode(true); try { $share = $this->shareManager->getShareByToken($token); } catch (ShareNotFound $e) { throw new CheckException($e->getMessage(), Http::STATUS_NOT_FOUND); } $this->checkShareIsValid($share, $token); $this->checkItemType($share); return $share; } /** * Makes sure that the token contains all the information that we need * * @param IShare $share * @param string $token * * @throws CheckException */ private function checkShareIsValid($share, $token) { if ($share->getShareOwner() === null || $share->getTarget() === null ) { $message = 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")'; throw new CheckException($message, Http::STATUS_NOT_FOUND); } } /** * Makes sure an item type was set for that token * * @param IShare $share * * @throws CheckException */ private function checkItemType($share) { if ($share->getNodeType() === null) { $message = 'No item type set for share id: ' . $share->getId(); throw new CheckException($message, Http::STATUS_NOT_FOUND); } } /** * Checks if a password is required or if the one supplied is working * * @param IShare $share * @param string|null $password optional password * * @throws CheckException */ private function checkAuthorisation($share, $password) { $passwordRequired = $share->getPassword(); if (isset($passwordRequired)) { if ($password !== null) { $this->authenticate($share, $password); } else { $this->checkSession($share); } } } /** * Authenticate link item with the given password * or with the session if no password was given. * * @param IShare $share * @param string $password * * @return bool true if authorized, an exception is raised otherwise * * @throws CheckException */ private function authenticate($share, $password) { if ((int)$share->getShareType() === Share::SHARE_TYPE_LINK) { $this->checkPassword($share, $password); } else { throw new CheckException( 'Unknown share type ' . $share->getShareType() . ' for share id ' . $share->getId(), Http::STATUS_NOT_FOUND ); } return true; } /** * Validates the given password * * @fixme @LukasReschke says: Migrate old hashes to new hash format * Due to the fact that there is no reasonable functionality to update the password * of an existing share no migration is yet performed there. * The only possibility is to update the existing share which will result in a new * share ID and is a major hack. * * In the future the migration should be performed once there is a proper method * to update the share's password. (for example `$share->updatePassword($password)` * * @link https://github.com/owncloud/core/issues/10671 * * @param IShare $share * @param string $password * * @throws CheckException */ private function checkPassword($share, $password) { $newHash = ''; if ($this->shareManager->checkPassword($share, $password)) { // Save item id in session for future requests $this->session->set('public_link_authenticated', (string)$share->getId()); // @codeCoverageIgnoreStart if (!empty($newHash)) { // For future use } // @codeCoverageIgnoreEnd } else { throw new CheckException("Wrong password", Http::STATUS_UNAUTHORIZED); } } /** * Makes sure the user is already properly authenticated when a password is required and none * was provided * * @param IShare $share * * @throws CheckException */ private function checkSession($share) { // Not authenticated ? if (!$this->session->exists('public_link_authenticated') || $this->session->get('public_link_authenticated') !== (string)$share->getId() ) { throw new CheckException("Missing password", Http::STATUS_UNAUTHORIZED); } } } Middleware/CheckException.php 0000604 00000001361 15247115507 0012232 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Middleware; use OCP\Util; /** * Thrown when one of the tests in the "check" middlewares fails * * @package OCA\Gallery\Middleware */ class CheckException extends \Exception { /** * Constructor * * @param string $msg the message contained in the exception * @param int $code the HTTP status code */ public function __construct($msg, $code = 0) { Util::writeLog('gallery', 'Exception: ' . $msg . ' (' . $code . ')', Util::ERROR); parent::__construct($msg, $code); } } Middleware/CheckMiddleware.php 0000604 00000010126 15247115507 0012350 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * @author Bernhard Posselt <dev@bernhard-posselt.com> * * @copyright Olivier Paroz 2017 * @copyright Bernhard Posselt 2017 */ namespace OCA\Gallery\Middleware; use OCP\IURLGenerator; use OCP\IRequest; use OCP\ILogger; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Middleware; /** * Checks that we have a valid token linked to a valid resource and that the * user is authorised to access it * * @package OCA\Gallery\Middleware */ abstract class CheckMiddleware extends Middleware { /** @var string */ protected $appName; /** @var IRequest */ protected $request; /** @var IURLGenerator */ private $urlGenerator; /** @var ILogger */ protected $logger; /*** * Constructor * * @param string $appName * @param IRequest $request * @param IURLGenerator $urlGenerator * @param ILogger $logger */ public function __construct( $appName, IRequest $request, IURLGenerator $urlGenerator, ILogger $logger ) { $this->appName = $appName; $this->request = $request; $this->urlGenerator = $urlGenerator; $this->logger = $logger; } /** * If a CheckException is being caught, clients who sent an ajax requests * get a JSON error response while the others are redirected to an error * page * * @inheritDoc */ public function afterException($controller, $methodName, \Exception $exception) { if ($exception instanceof CheckException) { $message = $exception->getMessage(); $code = $exception->getCode(); $this->logger->debug("[TokenCheckException] {exception}", ['exception' => $message]); return $this->computeResponse($message, $code); } throw $exception; } /** * Decides which type of response to send * * @param string $message * @param int $code * * @return JSONResponse|RedirectResponse|TemplateResponse */ private function computeResponse($message, $code) { $acceptHtml = stripos($this->request->getHeader('Accept'), 'html'); if ($acceptHtml === false) { $response = $this->sendJsonResponse($message, $code); } else { $response = $this->sendHtmlResponse($message, $code); } return $response; } /** * Redirects the client to an error page or shows an authentication form * * @param string $message * @param int $code * * @return RedirectResponse|TemplateResponse */ private function sendHtmlResponse($message, $code) { $this->logger->debug("[CheckException] HTML response"); /** * We need to render a template for 401 or we'll have an endless loop as * this is called before the controller gets a chance to render anything */ if ($code === 401) { $response = $this->sendHtml401(); } else { $response = $this->redirectToErrorPage($message, $code); } return $response; } /** * Shows an authentication form * * @return TemplateResponse */ private function sendHtml401() { $params = $this->request->getParams(); $this->logger->debug( '[CheckException] Unauthorised Request params: {params}', ['params' => $params] ); return new TemplateResponse($this->appName, 'authenticate', $params, 'guest'); } /** * Redirects the client to an error page * * @param string $message * @param int $code * * @return RedirectResponse */ private function redirectToErrorPage($message, $code) { $url = $this->urlGenerator->linkToRoute( $this->appName . '.page.error_page', ['code' => $code] ); $response = new RedirectResponse($url); $response->addCookie('galleryErrorMessage', $message); return $response; } /** * Returns a JSON response to the client * * @param string $message * @param int $code * * @return JSONResponse */ private function sendJsonResponse($message, $code) { $this->logger->debug("[TokenCheckException] JSON response"); $jsonData = [ 'message' => $message, 'success' => false ]; return new JSONResponse($jsonData, $code); } } Middleware/SharingCheckMiddleware.php 0000604 00000004503 15247115507 0013666 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Lukas Reschke 2017 * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Middleware; use OCP\IConfig; use OCP\IRequest; use OCP\ILogger; use OCP\IURLGenerator; use OCP\AppFramework\Http; use OCP\AppFramework\Utility\IControllerMethodReflector; /** * Checks whether the "sharing check" is enabled * * @package OCA\Gallery\SharingCheckMiddleware */ class SharingCheckMiddleware extends CheckMiddleware { /** @var IConfig */ private $config; /** @var IControllerMethodReflector */ protected $reflector; /*** * Constructor * * @param string $appName * @param IRequest $request * @param IConfig $appConfig * @param IControllerMethodReflector $reflector * @param IURLGenerator $urlGenerator * @param ILogger $logger */ public function __construct( $appName, IRequest $request, IConfig $appConfig, IControllerMethodReflector $reflector, IURLGenerator $urlGenerator, ILogger $logger ) { parent::__construct( $appName, $request, $urlGenerator, $logger ); $this->config = $appConfig; $this->reflector = $reflector; } /** * Checks if sharing is enabled before the controllers is executed * * Inspects the controller method annotations and if PublicPage is found * it makes sure that sharing is enabled in the configuration settings * * The check is not performed on "guest" pages which don't require sharing * to be enabled * * @inheritDoc */ public function beforeController($controller, $methodName) { if ($this->reflector->hasAnnotation('Guest')) { return; } $sharingEnabled = $this->isSharingEnabled(); $isPublicPage = $this->reflector->hasAnnotation('PublicPage'); if ($isPublicPage && !$sharingEnabled) { throw new CheckException("'Sharing is disabled'", Http::STATUS_SERVICE_UNAVAILABLE); } } /** * Checks whether public sharing (via links) is enabled * * @return bool */ private function isSharingEnabled() { $shareApiAllowLinks = $this->config->getAppValue('core', 'shareapi_allow_links', 'yes'); if ($shareApiAllowLinks !== 'yes') { return false; } return true; } } Service/SearchMediaService.php 0000604 00000013211 15247115507 0012344 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; use OCP\Files\Folder; use OCP\Files\File; /** * Searches the instance for media files which can be shown * * @package OCA\Gallery\Service */ class SearchMediaService extends FilesService { /** @var null|array<string,string|int> */ private $images = []; /** @var null|array<string,string|int> */ private $albums = []; /** @var string[] */ private $supportedMediaTypes; /** * This returns the list of all media files which can be shown starting from the given folder * * @param Folder $folderNode the current album * @param string[] $supportedMediaTypes the list of supported media types * @param array $features the list of supported features * * @return array<null|array<string,string|int>> all the images we could find */ public function getMediaFiles($folderNode, $supportedMediaTypes, $features) { $this->supportedMediaTypes = $supportedMediaTypes; $this->features = $features; $this->searchFolder($folderNode); return [$this->images, $this->albums]; } /** * Look for media files and folders in the given folder * * @param Folder $folder * @param int $subDepth * * @return int */ private function searchFolder($folder, $subDepth = 0) { $albumImageCounter = 0; $subFolders = []; $this->addFolderToAlbumsArray($folder); $nodes = $this->getNodes($folder, $subDepth); foreach ($nodes as $node) { if (!$this->isAllowedAndAvailable($node)) { continue; } $nodeType = $this->getNodeType($node); $subFolders = array_merge($subFolders, $this->getAllowedSubFolder($node, $nodeType)); $albumImageCounter = $this->addMediaFile($node, $nodeType, $albumImageCounter); if ($this->haveEnoughPictures($albumImageCounter, $subDepth)) { break; } } $albumImageCounter = $this->searchSubFolders($subFolders, $subDepth, $albumImageCounter); return $albumImageCounter; } /** * Adds the node to the list of images if it's a file and we can generate a preview of it * * @param File|Folder $node * @param string $nodeType * @param int $albumImageCounter * * @return int */ private function addMediaFile($node, $nodeType, $albumImageCounter) { if ($nodeType === 'file') { $albumImageCounter = $albumImageCounter + (int)$this->isPreviewAvailable($node); } return $albumImageCounter; } /** * Checks if we've collected enough pictures to be able to build the view * * An album is full when we find max 4 pictures at the same level * * @param int $albumImageCounter * @param int $subDepth * * @return bool */ private function haveEnoughPictures($albumImageCounter, $subDepth) { if ($subDepth === 0) { return false; } return $albumImageCounter === 4; } /** * Looks for pictures in sub-folders * * If we're at level 0, we need to look for pictures in sub-folders no matter what * If we're at deeper levels, we only need to go further if we haven't managed to find one * picture in the current folder * * @param array <Folder> $subFolders * @param int $subDepth * @param int $albumImageCounter * * @return int */ private function searchSubFolders($subFolders, $subDepth, $albumImageCounter) { if ($this->folderNeedsToBeSearched($subFolders, $subDepth, $albumImageCounter)) { $subDepth++; foreach ($subFolders as $subFolder) { //$this->logger->debug("Sub-Node path : {path}", ['path' => $subFolder->getPath()]); $albumImageCounter = $this->searchFolder($subFolder, $subDepth); if ($this->abortSearch($subDepth, $albumImageCounter)) { break; } } } return $albumImageCounter; } /** * Checks if we need to look for media files in the specified folder * * @param array <Folder> $subFolders * @param int $subDepth * @param int $albumImageCounter * * @return bool */ private function folderNeedsToBeSearched($subFolders, $subDepth, $albumImageCounter) { return !empty($subFolders) && ($subDepth === 0 || $albumImageCounter === 0); } /** * Returns true if there is no need to check any other sub-folder at the same depth level * * @param int $subDepth * @param int $count * * @return bool */ private function abortSearch($subDepth, $count) { return $subDepth > 1 && $count > 0; } /** * Returns true if the file is of a supported media type and adds it to the array of items to * return * * @todo We could potentially check if the file is readable ($file->stat() maybe) in order to * only return valid files, but this may slow down operations * * @param File $file the file to test * * @return bool */ private function isPreviewAvailable($file) { try { $mimeType = $file->getMimeType(); if (in_array($mimeType, $this->supportedMediaTypes)) { $this->addFileToImagesArray($mimeType, $file); return true; } } catch (\Exception $exception) { return false; } return false; } /** * Adds a folder to the albums array * * @param Folder $folder the folder to add to the albums array */ private function addFolderToAlbumsArray($folder) { $albumData = $this->getFolderData($folder); $this->albums[$albumData['path']] = $albumData; } /** * Adds a file to the images array * * @param string $mimeType the media type of the file to add to the images array * @param File $file the file to add to the images array */ private function addFileToImagesArray($mimeType, $file) { $imageData = $this->getNodeData($file); $imageData['mimetype'] = $mimeType; $this->images[] = $imageData; } } Service/NotFoundServiceException.php 0000604 00000000601 15247115507 0013611 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; /** * Thrown when the service cannot find a node */ class NotFoundServiceException extends ServiceException {} Service/DownloadService.php 0000604 00000002140 15247115507 0011745 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; use OCP\Files\File; /** * Prepares the file to download * * @package OCA\Gallery\Service */ class DownloadService extends Service { use Base64Encode; /** * Downloads the requested file * * @param File $file * @param bool $base64Encode * * @return array|false * @throws NotFoundServiceException */ public function downloadFile($file, $base64Encode = false) { try { $this->logger->debug( "[DownloadService] File to Download: {name}", ['name' => $file->getName()] ); $download = [ 'preview' => $file->getContent(), 'mimetype' => $file->getMimeType() ]; if ($base64Encode) { $download['preview'] = $this->encode($download['preview']); } return $download; } catch (\Exception $exception) { throw new NotFoundServiceException('There was a problem accessing the file'); } } } Service/ForbiddenServiceException.php 0000604 00000000616 15247115507 0013757 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; /** * Thrown when the service does not have access to a node */ class ForbiddenServiceException extends ServiceException {} Service/ConfigService.php 0000604 00000024124 15247115507 0011411 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; use OCP\Files\Folder; use OCP\IPreview; use OCP\ILogger; use OCA\Gallery\Config\ConfigParser; use OCA\Gallery\Config\ConfigException; use OCA\Gallery\Environment\Environment; /** * Finds configurations files and returns a configuration array * * Checks the current and parent folders for configuration files and to see if we're allowed to * look for media file * Supports explicit inheritance * * @package OCA\Gallery\Service */ class ConfigService extends FilesService { /** @var string */ private $configName = 'gallery.cnf'; /** @var array <string,bool> */ private $completionStatus = ['design' => false, 'information' => false, 'sorting' => false]; /** @var ConfigParser */ private $configParser; /** @var IPreview */ private $previewManager; /** * @todo This hard-coded array could be replaced by admin settings * * @var string[] */ private $baseMimeTypes = [ 'image/png', 'image/jpeg', 'image/gif', 'image/x-xbitmap', 'image/bmp', 'image/tiff', 'image/x-dcraw', 'application/x-photoshop', 'application/illustrator', 'application/postscript', ]; /** * These types are useful for files preview in the files app, but * not for the gallery side * * @var string[] */ private $slideshowMimeTypes = [ 'application/font-sfnt', 'application/x-font', ]; /** * Constructor * * @param string $appName * @param Environment $environment * @param ConfigParser $configParser * @param IPreview $previewManager * @param ILogger $logger */ public function __construct( $appName, Environment $environment, ConfigParser $configParser, IPreview $previewManager, ILogger $logger ) { parent::__construct($appName, $environment, $logger); $this->configParser = $configParser; $this->previewManager = $previewManager; } /** * Returns a list of supported features * * @return string[] */ public function getFeaturesList() { $featuresList = []; /** @var Folder $rootFolder */ $rootFolder = $this->environment->getVirtualRootFolder(); if ($this->isAllowedAndAvailable($rootFolder) && $this->configExists($rootFolder)) { try { $featuresList = $this->configParser->getFeaturesList($rootFolder, $this->configName); } catch (ConfigException $exception) { $featuresList = $this->buildErrorMessage($exception, $rootFolder); } } return $featuresList; } /** * This builds and returns a list of all supported media types * * @todo Native SVG could be disabled via admin settings * * @param bool $extraMediaTypes * @param bool $nativeSvgSupport * * @return string[] all supported media types */ public function getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport) { $supportedMimes = []; $wantedMimes = $this->baseMimeTypes; if ($extraMediaTypes) { $wantedMimes = array_merge($wantedMimes, $this->slideshowMimeTypes); } foreach ($wantedMimes as $wantedMime) { // Let's see if a preview of files of that media type can be generated if ($this->isMimeSupported($wantedMime)) { // We store the media type $supportedMimes[] = $wantedMime; } } $supportedMimes = $this->addSvgSupport($supportedMimes, $nativeSvgSupport); //$this->logger->debug("Supported Mimes: {mimes}", ['mimes' => $supportedMimes]); return $supportedMimes; } /** * Returns the configuration of the currently selected folder * * * information (description, copyright) * * sorting (date, name, inheritance) * * design (colour) * * if the album should be ignored * * @param Folder $folderNode the current folder * @param array $features the list of features retrieved fro the configuration file * * @return array|null * @throws ForbiddenServiceException */ public function getConfig($folderNode, $features) { $this->features = $features; list ($albumConfig, $ignored) = $this->collectConfig($folderNode, $this->ignoreAlbum, $this->configName); if ($ignored) { throw new ForbiddenServiceException( 'The owner has placed a restriction or the storage location is unavailable' ); } return $albumConfig; } /** * Throws an exception if the media type of the file is not part of what the app allows * * @param $mimeType * * @throws ForbiddenServiceException */ public function validateMimeType($mimeType) { if (!in_array($mimeType, $this->getSupportedMediaTypes(true, true))) { throw new ForbiddenServiceException('Media type not allowed'); } } /** * Determines if we have a configuration file to work with * * @param Folder $rootFolder the virtual root folder * * @return bool */ private function configExists($rootFolder) { return $rootFolder && $rootFolder->nodeExists($this->configName); } /** * Adds the SVG media type if it's not already there * * If it's enabled, but doesn't work, an exception will be raised when trying to generate a * preview. If it's disabled, we support it via the browser's native support * * @param string[] $supportedMimes * @param bool $nativeSvgSupport * * @return string[] */ private function addSvgSupport($supportedMimes, $nativeSvgSupport) { if (!in_array('image/svg+xml', $supportedMimes) && $nativeSvgSupport) { $supportedMimes[] = 'image/svg+xml'; } return $supportedMimes; } /** * Returns true if the passed mime type is supported * * In case of a failure, we just return that the media type is not supported * * @param string $mimeType * * @return boolean */ private function isMimeSupported($mimeType = '*') { try { return $this->previewManager->isMimeSupported($mimeType); } catch (\Exception $exception) { unset($exception); return false; } } /** * Returns an album configuration array * * Goes through all the parent folders until either we're told the album is private or we've * reached the root folder * * @param Folder $folder the current folder * @param string $ignoreAlbum name of the file which blacklists folders * @param string $configName name of the configuration file * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder * @param array $configSoFar the configuration collected so far * * @return array <null|array,bool> */ private function collectConfig( $folder, $ignoreAlbum, $configName, $level = 0, $configSoFar = [] ) { if ($folder->nodeExists($ignoreAlbum)) { // Cancel as soon as we find out that the folder is private or external return [null, true]; } $isRootFolder = $this->isRootFolder($folder, $level); if ($folder->nodeExists($configName)) { $configSoFar = $this->buildFolderConfig($folder, $configName, $configSoFar, $level); } if (!$isRootFolder) { return $this->getParentConfig($folder, $ignoreAlbum, $configName, $level, $configSoFar); } $configSoFar = $this->validatesInfoConfig($configSoFar); // We have reached the root folder return [$configSoFar, false]; } /** * Returns a parsed configuration if one was found in the current folder or generates an error * message to send back * * @param Folder $folder the current folder * @param string $configName name of the configuration file * @param array $collectedConfig the configuration collected so far * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder * * @return array */ private function buildFolderConfig($folder, $configName, $collectedConfig, $level) { try { list($collectedConfig, $completionStatus) = $this->configParser->getFolderConfig( $folder, $configName, $collectedConfig, $this->completionStatus, $level ); $this->completionStatus = $completionStatus; } catch (ConfigException $exception) { $collectedConfig = $this->buildErrorMessage($exception, $folder); } return $collectedConfig; } /** * Builds the error message to send back when there is an error * * @fixme Missing translation * * @param ConfigException $exception * @param Folder $folder the current folder * * @return array<array<string,string>,bool> */ private function buildErrorMessage($exception, $folder) { $configPath = $this->environment->getPathFromVirtualRoot($folder); $errorMessage = $exception->getMessage() . ". Config location: /$configPath"; $this->logger->error($errorMessage); $config = ['error' => ['message' => $errorMessage]]; $completionStatus = $this->completionStatus; foreach ($completionStatus as $key) { $completionStatus[$key] = true; } $this->completionStatus = $completionStatus; return [$config]; } /** * Removes links if they were collected outside of the virtual root * * This is for shared folders which have a virtual root * * @param array $albumConfig * * @return array */ private function validatesInfoConfig($albumConfig) { $this->virtualRootLevel; if (array_key_exists('information', $albumConfig)) { $info = $albumConfig['information']; if (array_key_exists('level', $info)) { $level = $info['level']; if ($level > $this->virtualRootLevel) { $albumConfig['information']['description_link'] = null; $albumConfig['information']['copyright_link'] = null; } } } return $albumConfig; } /** * Looks for an album configuration in the parent folder * * We will look up to the virtual root of a shared folder, for privacy reasons * * @param Folder $folder the current folder * @param string $privacyChecker name of the file which blacklists folders * @param string $configName name of the configuration file * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder * @param array $collectedConfig the configuration collected so far * * @return array<null|array,bool> */ private function getParentConfig($folder, $privacyChecker, $configName, $level, $collectedConfig ) { $parentFolder = $folder->getParent(); $level++; return $this->collectConfig( $parentFolder, $privacyChecker, $configName, $level, $collectedConfig ); } } Service/InternalServerErrorServiceException.php 0000604 00000000633 15247115507 0016037 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; /** * Thrown when the service ran into an internal server error */ class InternalServerErrorServiceException extends ServiceException {} Service/ServiceException.php 0000604 00000001154 15247115507 0012140 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; use OCP\Util; /** * Thrown when the service cannot reply to a request */ class ServiceException extends \Exception { /** * Constructor * * @param string $msg the message contained in the exception */ public function __construct($msg) { Util::writeLog('gallery', 'Exception: ' . $msg, Util::ERROR); parent::__construct($msg); } } Service/Base64Encode.php 0000604 00000001561 15247115507 0011025 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; /** * Base64 encoding utility method * * @package OCA\Gallery\Service */ trait Base64Encode { /** * Returns base64 encoded data of a preview * * Using base64_encode for files which are downloaded * (cached Thumbnails, SVG, GIFs) and using __toStrings * for the previews which are instances of \OC_Image * * @param \OC_Image|string $previewData * * @return string */ protected function encode($previewData) { if ($previewData instanceof \OC_Image) { $previewData = (string)$previewData; } else { $previewData = base64_encode($previewData); } return $previewData; } } Service/FilesService.php 0000604 00000015534 15247115507 0011253 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\Node; /** * Contains various methods to retrieve information from the filesystem * * @package OCA\Gallery\Service */ abstract class FilesService extends Service { /** @var int */ protected $virtualRootLevel = null; /** @var string[] */ protected $features; /** @var string */ protected $ignoreAlbum = '.nomedia'; /** * Retrieves all files and sub-folders contained in a folder * * If we can't find anything in the current folder, we throw an exception as there is no point * in doing any more work, but if we're looking at a sub-folder, we return an empty array so * that it can be simply ignored * * @param Folder $folder * @param int $subDepth * * @return array */ protected function getNodes($folder, $subDepth) { try { $nodes = $folder->getDirectoryListing(); } catch (\Exception $exception) { $nodes = $this->recoverFromGetNodesError($subDepth, $exception); } return $nodes; } /** * Determines if the files are hosted locally (shared or not) and can be used by the preview * system * * isMounted() doesn't include externally hosted shares, so we need to exclude those from the * non-mounted nodes * * @param Node $node * * @return bool */ protected function isAllowedAndAvailable($node) { try { return $node && $this->isAllowed($node) && $this->isAvailable($node); } catch (\Exception $exception) { $message = 'The folder is not available: ' . $exception->getMessage(); $this->logger->error($message); return false; } } /** * Returns the node type, either 'dir' or 'file' * * If there is a problem, we return an empty string so that the node can be ignored * * @param Node $node * * @return string */ protected function getNodeType($node) { try { $nodeType = $node->getType(); } catch (\Exception $exception) { return ''; } return $nodeType; } /** * Returns various information about a node * * @param Node|File|Folder $node * * @return array<string,int|string|bool|array<string,int|string>> */ protected function getNodeData($node) { $imagePath = $this->environment->getPathFromVirtualRoot($node); $nodeId = $node->getId(); $mTime = $node->getMTime(); $etag = $node->getEtag(); $size = $node->getSize(); $sharedWithUser = $node->isShared(); $ownerData = $this->getOwnerData($node); $permissions = $node->getPermissions(); //$this->logger->debug("Image path : {var1}", ['var1' => $imagePath]); return $this->formatNodeData( $imagePath, $nodeId, $mTime, $etag, $size, $sharedWithUser, $ownerData, $permissions ); } /** * Returns various information about a folder * * @param Folder $node * * @return array<string,int|string|bool|array<string,int|string>> */ protected function getFolderData($node) { $folderData = $this->getNodeData($node); $folderData['freespace'] = $node->getFreeSpace(); return $folderData; } /** * Returns the node if it's a folder we have access to * * @param Folder $node * @param string $nodeType * * @return array|Folder */ protected function getAllowedSubFolder($node, $nodeType) { if ($nodeType === 'dir') { /** @var Folder $node */ if (!$node->nodeExists($this->ignoreAlbum)) { return [$node]; } } return []; } /** * Determines if we've reached the root folder * * @param Folder $folder * @param int $level * * @return bool */ protected function isRootFolder($folder, $level) { $isRootFolder = false; $rootFolder = $this->environment->getVirtualRootFolder(); if ($folder->getPath() === $rootFolder->getPath()) { $isRootFolder = true; } $virtualRootFolder = $this->environment->getPathFromVirtualRoot($folder); if (empty($virtualRootFolder)) { $this->virtualRootLevel = $level; } return $isRootFolder; } /** * Throws an exception if this problem occurs in the current folder, otherwise just ignores the * sub-folder * * @param int $subDepth * @param \Exception $exception * * @return array * @throws NotFoundServiceException */ private function recoverFromGetNodesError($subDepth, $exception) { if ($subDepth === 0) { throw new NotFoundServiceException($exception->getMessage()); } return []; } /** * Determines if we can consider the node mounted locally or if it's been authorised to be * scanned * * @param Node $node * * @return bool */ private function isAllowed($node) { $allowed = true; if ($this->isExternalShare($node)) { $allowed = $this->isExternalShareAllowed(); } if ($node->isMounted()) { $mount = $node->getMountPoint(); $allowed = $mount && $mount->getOption('previews', true); } return $allowed; } /** * Determines if the node is available, as in readable * * @todo Test to see by how much using file_exists slows things down * * @param Node $node * * @return bool */ private function isAvailable($node) { return $node->isReadable(); } /** * Determines if the user has allowed the use of external shares * * @return bool */ private function isExternalShareAllowed() { $rootFolder = $this->environment->getVirtualRootFolder(); return ($this->isExternalShare($rootFolder) || in_array('external_shares', $this->features)); } /** * Determines if the node is a share which is hosted externally * * * @param Node $node * * @return bool */ private function isExternalShare($node) { $sid = explode( ':', $node->getStorage() ->getId() ); return ($sid[0] === 'shared' && $sid[2][0] !== '/'); } /** * Returns what we known about the owner of a node * * @param Node $node * * @return null|array<string,int|string> */ private function getOwnerData($node) { $owner = $node->getOwner(); $ownerData = []; if ($owner) { $ownerData = [ 'uid' => $owner->getUID(), 'displayname' => $owner->getDisplayName() ]; } return $ownerData; } /** * Returns an array containing information about a node * * @param string $imagePath * @param int $nodeId * @param int $mTime * @param string $etag * @param int $size * @param bool $sharedWithUser * @param array <string,int|string> $ownerData * @param int $permissions * * @return array */ private function formatNodeData( $imagePath, $nodeId, $mTime, $etag, $size, $sharedWithUser, $ownerData, $permissions ) { return [ 'path' => $imagePath, 'nodeid' => $nodeId, 'mtime' => $mTime, 'etag' => $etag, 'size' => $size, 'sharedwithuser' => $sharedWithUser, 'owner' => $ownerData, 'permissions' => $permissions ]; } } Service/PreviewService.php 0000604 00000012716 15247115507 0011631 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; use OCP\Files\File; use OCP\Image; use OCP\IPreview; use OCP\ILogger; use OCA\Gallery\Environment\Environment; /** * Generates previews * * @package OCA\Gallery\Service */ class PreviewService extends Service { use Base64Encode; /** @var IPreview */ private $previewManager; /** * Constructor * * @param string $appName * @param Environment $environment * @param IPreview $previewManager * @param ILogger $logger */ public function __construct( $appName, Environment $environment, IPreview $previewManager, ILogger $logger ) { parent::__construct($appName, $environment, $logger); $this->previewManager = $previewManager; } /** * Decides if we should download the file instead of generating a preview * * @param File $file * @param bool $animatedPreview * * @return bool */ public function isPreviewRequired($file, $animatedPreview) { $mime = $file->getMimeType(); if ($mime === 'image/svg+xml') { return $this->isSvgPreviewRequired(); } if ($mime === 'image/gif') { return $this->isGifPreviewRequired($file, $animatedPreview); } return true; } /** * Returns an array containing everything needed by the client to be able to display a preview * * * fileid: the file's ID * * mimetype: the file's media type * * preview: the preview's content * * Example logger * $this->logger->debug( * "[PreviewService] Path : {path} / mime: {mimetype} / fileid: {fileid}", * [ * 'path' => $preview['data']['path'], * 'mimetype' => $preview['data']['mimetype'], * 'fileid' => $preview['fileid'] * ] * ); * * @todo Get the max size from the settings * * @param File $file * @param int $maxX asked width for the preview * @param int $maxY asked height for the preview * @param bool $keepAspect * @param bool $base64Encode * * @return string|\OC_Image|string|false preview data * @throws InternalServerErrorServiceException */ public function createPreview( $file, $maxX = 0, $maxY = 0, $keepAspect = true, $base64Encode = false ) { try { $preview = $this->previewManager->getPreview($file, $maxX, $maxY, !$keepAspect); $img = new Image($preview->getContent()); $mimeType = $img->mimeType(); if ($img && $base64Encode) { $img = $this->encode($img); } return [ 'preview' => $img, 'mimetype' => $mimeType ]; } catch (\Exception $exception) { throw new InternalServerErrorServiceException('Preview generation has failed'); } } /** * Returns true if the passed mime type is supported * * In case of a failure, we just return that the media type is not supported * * @param string $mimeType * * @return boolean */ private function isMimeSupported($mimeType = '*') { try { return $this->previewManager->isMimeSupported($mimeType); } catch (\Exception $exception) { unset($exception); return false; } } /** * Decides if we should download the SVG or generate a preview * * SVGs are downloaded if the SVG converter is disabled * Files of any media type are downloaded if requested by the client * * @return bool */ private function isSvgPreviewRequired() { return $this->isMimeSupported('image/svg+xml'); } /** * Decides if we should download the GIF or generate a preview * * GIFs are downloaded if they're animated and we want to show * animations * * @param File $file * @param bool $animatedPreview * * @return bool */ private function isGifPreviewRequired($file, $animatedPreview) { $gifSupport = $this->isMimeSupported('image/gif'); $animatedGif = $this->isGifAnimated($file); return $gifSupport && !($animatedGif && $animatedPreview); } /** * Tests if a GIF is animated * * An animated gif contains multiple "frames", with each frame having a * header made up of: * * a static 4-byte sequence (\x00\x21\xF9\x04) * * 4 variable bytes * * a static 2-byte sequence (\x00\x2C) (Photoshop uses \x00\x21) * * We read through the file until we reach the end of the file, or we've * found at least 2 frame headers * * @link http://php.net/manual/en/function.imagecreatefromgif.php#104473 * * @param File $file * * @return bool */ private function isGifAnimated($file) { $count = 0; $fileHandle = $this->isFileReadable($file); if ($fileHandle) { while (!feof($fileHandle) && $count < 2) { $chunk = fread($fileHandle, 1024 * 100); //read 100kb at a time $count += preg_match_all( '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches ); } fclose($fileHandle); } return $count > 1; } /** * Determines if we can read the content of the file and returns a file pointer resource * * We can't use something like $node->isReadable() as it's too unreliable * Some storage classes just check for the presence of the file * * @param File $file * * @return resource * @throws InternalServerErrorServiceException */ private function isFileReadable($file) { try { $fileHandle = $file->fopen('rb'); if (!$fileHandle) { throw new \Exception(); } } catch (\Exception $exception) { throw new InternalServerErrorServiceException( 'Something went wrong when trying to read' . $file->getPath() ); } return $fileHandle; } } Service/Service.php 0000604 00000004016 15247115507 0010261 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; use OCP\Files\Node; use OCP\ILogger; use OCA\Gallery\Environment\Environment; /** * Contains methods which all services will need * * @package OCA\Gallery\Service */ abstract class Service { /** * @var string */ protected $appName; /** * @var Environment */ protected $environment; /** * @var ILogger */ protected $logger; /** * Constructor * * @param string $appName * @param Environment $environment * @param ILogger $logger */ public function __construct( $appName, Environment $environment, ILogger $logger ) { $this->appName = $appName; $this->environment = $environment; $this->logger = $logger; } /** * Returns the file matching the given ID * * @param int $nodeId ID of the resource to locate * * @return Node * @throws NotFoundServiceException */ public function getFile($nodeId) { $node = $this->getNode($nodeId); if ($node->getType() === 'file') { $this->validateNode($node); return $node; } else { throw new NotFoundServiceException("Cannot find a file with this ID"); } } /** * Returns the node matching the given ID * * @param int $nodeId ID of the resource to locate * * @return Node * @throws NotFoundServiceException */ private function getNode($nodeId) { try { $node = $this->environment->getResourceFromId($nodeId); return $node; } catch (\Exception $exception) { throw new NotFoundServiceException($exception->getMessage()); } } /** * Makes extra sure that we can actually do something with the file * * @param Node $node * * @throws NotFoundServiceException */ private function validateNode($node) { if (!$node->getMimetype() || !$node->isReadable()) { throw new NotFoundServiceException("Can't access the file"); } } } Service/ThumbnailService.php 0000604 00000002064 15247115507 0012126 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; /** * Deals with any thumbnail specific requests * * @package OCA\Gallery\Service */ class ThumbnailService { /** * @var bool */ private $animatedPreview = false; /** * @var bool */ private $base64Encode = true; /** * Returns thumbnail specs * * * Album thumbnails need to be 200x200 and some will be resized by the * browser to 200x100 or 100x100. * * Standard thumbnails are 400x200. * * @param bool $square * @param double $scale * * @return array<double|boolean> */ public function getThumbnailSpecs($square, $scale) { $height = ceil(200 * $scale); if ($square) { $width = $height; } else { $width = 2 * $height; } $thumbnail = [$width, $height, !$square, $this->animatedPreview, $this->base64Encode]; return $thumbnail; } } Service/SearchFolderService.php 0000604 00000005663 15247115507 0012554 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Service; use OCP\Files\Folder; use OCA\Gallery\Environment\NotFoundEnvException; /** * Looks for the folder to use, based on the request made by the client * * This is to make sure we were not: * * given a file * * given a folder name with a typo * * @package OCA\Gallery\Service */ class SearchFolderService extends FilesService { /** * @var int */ protected $virtualRootLevel = null; /** * This returns what we think is the current folder node based on a given path * * @param string $location * @param string[] $features * * @return array <string,Folder,bool> */ public function getCurrentFolder($location, $features) { $this->features = $features; return $this->findFolder($location); } /** * This returns the current folder node based on a path * * If the path leads to a file, we'll return the node of the containing folder * * If we can't find anything, we try with the parent folder, up to the root or until we reach * our recursive limit * * @param string $location * @param int $depth * * @return array <string,Folder,bool> */ private function findFolder($location, $depth = 0) { $node = null; $location = $this->validateLocation($location, $depth); try { $node = $this->environment->getNodeFromVirtualRoot($location); if ($node->getType() === 'file') { $node = $node->getParent(); } } catch (NotFoundEnvException $exception) { // There might be a typo in the file or folder name $folder = pathinfo($location, PATHINFO_DIRNAME); $depth++; return $this->findFolder($folder, $depth); } $path = $this->environment->getPathFromVirtualRoot($node); return $this->sendFolder($path, $node); } /** * Makes sure we don't go too far up before giving up * * @param string $location * @param int $depth * * @return string */ private function validateLocation($location, $depth) { if ($depth === 4) { // We can't find anything, so we decide to return data for the root folder $location = ''; } return $location; } /** * Makes sure that the folder is not empty, does meet our requirements in terms of location and * returns details about it * * @param string $path * @param Folder $node * * @return array <string,Folder,bool> * @throws ForbiddenServiceException|NotFoundServiceException */ private function sendFolder($path, $node) { if (is_null($node)) { // Something very wrong has just happened throw new NotFoundServiceException('Oh Nooooes!'); } elseif (!$this->isAllowedAndAvailable($node)) { throw new ForbiddenServiceException( 'The owner has placed a restriction or the storage location is unavailable' ); } return [$path, $node]; } } Controller/PageController.php 0000604 00000002341 15247115507 0012323 0 ustar 00 <?php /** * @author Robin Appelman <icewind@owncloud.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\LogReader\Controller; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; /** * Class PageController * * @package OCA\LogReader\Controller */ class PageController extends Controller { /** * @NoCSRFRequired * * @return TemplateResponse */ public function index() { $response = new TemplateResponse( $this->appName, 'index', [ 'appId' => $this->appName , 'inline-settings' => 'false' ] ); return $response; } } Controller/PreviewApiController.php 0000604 00000007246 15247115507 0013533 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; use OCP\IRequest; use OCP\IURLGenerator; use OCP\ILogger; use OCP\Files\File; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCA\Gallery\Http\ImageResponse; use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\ThumbnailService; use OCA\Gallery\Service\PreviewService; use OCA\Gallery\Service\DownloadService; use OCA\Gallery\Utility\EventSource; /** * Class PreviewApiController * * @package OCA\Gallery\Controller */ class PreviewApiController extends ApiController { use Preview; /** @var EventSource */ private $eventSource; /** * Constructor * * @param string $appName * @param IRequest $request * @param IURLGenerator $urlGenerator * @param ConfigService $configService * @param ThumbnailService $thumbnailService * @param PreviewService $previewService * @param DownloadService $downloadService * @param EventSource $eventSource * @param ILogger $logger */ public function __construct( $appName, IRequest $request, IURLGenerator $urlGenerator, ConfigService $configService, ThumbnailService $thumbnailService, PreviewService $previewService, DownloadService $downloadService, EventSource $eventSource, ILogger $logger ) { parent::__construct($appName, $request); $this->urlGenerator = $urlGenerator; $this->configService = $configService; $this->thumbnailService = $thumbnailService; $this->previewService = $previewService; $this->downloadService = $downloadService; $this->eventSource = $eventSource; $this->logger = $logger; } /** * @NoAdminRequired * @NoCSRFRequired * @CORS * * Generates thumbnails * * @see PreviewController::getThumbnails() * * @param string $ids the ID of the files of which we need thumbnail previews of * @param bool $square * @param double $scale * * @return array<string,array|string|null> */ public function getThumbnails($ids, $square, $scale) { $idsArray = explode(';', $ids); foreach ($idsArray as $id) { // Casting to integer here instead of using array_map to extract IDs from the URL list($thumbnail, $status) = $this->getThumbnail((int)$id, $square, $scale); $thumbnail['fileid'] = $id; $thumbnail['status'] = $status; $this->eventSource->send('preview', $thumbnail); } $this->eventSource->close(); $this->exitController(); // @codeCoverageIgnoreStart } // @codeCoverageIgnoreEnd /** * @NoAdminRequired * @NoCSRFRequired * @CORS * * Sends either a large preview of the requested file or the original file itself * * @param int $fileId the ID of the file of which we need a large preview of * @param int $width * @param int $height * @param bool $nativesvg This is a GET parameter, so no camelCase * * @return ImageResponse|Http\JSONResponse */ public function getPreview($fileId, $width, $height, $nativesvg = false) { /** @type File $file */ list($file, $preview, $status) = $this->getData($fileId, $width, $height); if (!$preview) { return new JSONResponse( [ 'message' => "I'm truly sorry, but we were unable to generate a preview for this file", 'success' => false ], $status ); } $preview['name'] = $file->getName(); // That's the only exception out of all the image media types we serve if ($preview['mimetype'] === 'image/svg+xml' && !$nativesvg) { $preview['mimetype'] = 'text/plain'; } return new ImageResponse($preview, $status); } } Controller/HttpError.php 0000604 00000005150 15247115507 0011335 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Bernhard Posselt 2017 * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; use Exception; use OCP\ILogger; use OCP\IRequest; use OCP\IURLGenerator; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\RedirectResponse; use OCA\Gallery\Environment\NotFoundEnvException; use OCA\Gallery\Service\NotFoundServiceException; use OCA\Gallery\Service\ForbiddenServiceException; /** * Our classes extend both Controller and ApiController, so we need to use * traits to add some common methods * * @package OCA\Gallery\Controller */ trait HttpError { /** * @param \Exception $exception * @param IRequest $request * @param ILogger $logger * * @return JSONResponse */ public function jsonError(Exception $exception, IRequest $request, ILogger $logger) { $code = $this->getHttpStatusCode($exception); // If the exception is not of type ForbiddenServiceException only show a // generic error message to avoid leaking information. if(!($exception instanceof ForbiddenServiceException)) { $logger->logException($exception, ['app' => 'gallery']); $message = sprintf('An error occurred. Request ID: %s', $request->getId()); } else { $message = $exception->getMessage() . ' (' . $code . ')'; } return new JSONResponse( [ 'message' => $message, 'success' => false, ], $code ); } /** * @param IURLGenerator $urlGenerator * @param string $appName * @param \Exception $exception * * @return RedirectResponse */ public function htmlError($urlGenerator, $appName, Exception $exception) { $message = $exception->getMessage(); $code = $this->getHttpStatusCode($exception); $url = $urlGenerator->linkToRoute( $appName . '.page.error_page', ['code' => $code] ); $response = new RedirectResponse($url); $response->addCookie('galleryErrorMessage', $message); return $response; } /** * Returns an error array * * @param $exception * * @return array<null|int|string> */ public function getHttpStatusCode($exception) { $code = Http::STATUS_INTERNAL_SERVER_ERROR; if ($exception instanceof NotFoundServiceException || $exception instanceof NotFoundEnvException ) { $code = Http::STATUS_NOT_FOUND; } if ($exception instanceof ForbiddenServiceException) { $code = Http::STATUS_FORBIDDEN; } return $code; } } Controller/ConfigController.php 0000604 00000002447 15247115507 0012663 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; use OCP\IRequest; use OCP\ILogger; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCA\Gallery\Service\ConfigService; /** * Class ConfigController * * @package OCA\Gallery\Controller */ class ConfigController extends Controller { use Config; use HttpError; /** * Constructor * * @param string $appName * @param IRequest $request * @param ConfigService $configService * @param ILogger $logger */ public function __construct( $appName, IRequest $request, ConfigService $configService, ILogger $logger ) { parent::__construct($appName, $request); $this->configService = $configService; $this->logger = $logger; } /** * @NoAdminRequired * * Returns an app configuration array * * @param bool $extramediatypes * * @return array <string,null|array> */ public function get($extramediatypes = false) { try { return $this->getConfig($extramediatypes); } catch (\Exception $exception) { return $this->jsonError($exception, $this->request, $this->logger); } } } Controller/FilesController.php 0000604 00000007563 15247115507 0012524 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; use OCP\IRequest; use OCP\IURLGenerator; use OCP\ILogger; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\RedirectResponse; use OCA\Gallery\Http\ImageResponse; use OCA\Gallery\Service\SearchFolderService; use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\SearchMediaService; use OCA\Gallery\Service\DownloadService; use OCA\Gallery\Service\ServiceException; /** * Class FilesController * * @package OCA\Gallery\Controller */ class FilesController extends Controller { use Files; use HttpError; /** @var IURLGenerator */ private $urlGenerator; /** * Constructor * * @param string $appName * @param IRequest $request * @param IURLGenerator $urlGenerator * @param SearchFolderService $searchFolderService * @param ConfigService $configService * @param SearchMediaService $searchMediaService * @param DownloadService $downloadService * @param ILogger $logger */ public function __construct( $appName, IRequest $request, IURLGenerator $urlGenerator, SearchFolderService $searchFolderService, ConfigService $configService, SearchMediaService $searchMediaService, DownloadService $downloadService, ILogger $logger ) { parent::__construct($appName, $request); $this->urlGenerator = $urlGenerator; $this->searchFolderService = $searchFolderService; $this->configService = $configService; $this->searchMediaService = $searchMediaService; $this->downloadService = $downloadService; $this->logger = $logger; } /** * @NoAdminRequired * * Returns a list of all media files available to the authenticated user * * * Authentication can be via a login/password or a token/(password) * * For private galleries, it returns all media files, with the full path from the root * folder For public galleries, the path starts from the folder the link gives access to * (virtual root) * * An exception is only caught in case something really wrong happens. As we don't test * files before including them in the list, we may return some bad apples * * @param string $location a path representing the current album in the app * @param string $features the list of supported features * @param string $etag the last known etag in the client * @param string $mediatypes the list of supported media types * * @return array <string,array<string,string|int>>|Http\JSONResponse */ public function getList($location, $features, $etag, $mediatypes) { $featuresArray = explode(';', $features); $mediaTypesArray = explode(';', $mediatypes); try { return $this->getFilesAndAlbums($location, $featuresArray, $etag, $mediaTypesArray); } catch (\Exception $exception) { return $this->jsonError($exception, $this->request, $this->logger); } } /** * @NoAdminRequired * * Sends the file matching the fileId * * @param int $fileId the ID of the file we want to download * @param string|null $filename * * @return ImageResponse */ public function download($fileId, $filename = null) { try { $download = $this->getDownload($fileId, $filename); } catch (ServiceException $exception) { $code = $this->getHttpStatusCode($exception); $url = $this->urlGenerator->linkToRoute( $this->appName . '.page.error_page', ['code' => $code] ); $response = new RedirectResponse($url); $response->addCookie('galleryErrorMessage', $exception->getMessage()); return $response; } // That's the only exception out of all the image media types we serve if ($download['mimetype'] === 'image/svg+xml') { $download['mimetype'] = 'text/plain'; } return new ImageResponse($download); } } Controller/ConfigApiController.php 0000604 00000002521 15247115507 0013306 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; use OCP\IRequest; use OCP\ILogger; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCA\Gallery\Service\ConfigService; /** * Class ConfigApiController * * @package OCA\Gallery\Controller */ class ConfigApiController extends ApiController { use Config; use HttpError; /** * Constructor * * @param string $appName * @param IRequest $request * @param ConfigService $configService * @param ILogger $logger */ public function __construct( $appName, IRequest $request, ConfigService $configService, ILogger $logger ) { parent::__construct($appName, $request); $this->configService = $configService; $this->logger = $logger; } /** * @NoAdminRequired * @NoCSRFRequired * @CORS * * Returns an app configuration array * * @param bool $extramediatypes * * @return array <string,null|array> */ public function get($extramediatypes = false) { try { return $this->getConfig($extramediatypes); } catch (\Exception $exception) { return $this->jsonError($exception, $this->request, $this->logger); } } } Controller/Config.php 0000604 00000002656 15247115507 0010621 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; use OCP\ILogger; use OCP\AppFramework\Http; use OCA\Gallery\Service\ConfigService; /** * Trait Config * * @package OCA\Gallery\Controller */ trait Config { /** * @var ConfigService */ private $configService; /** * @var ILogger */ private $logger; /** * @NoAdminRequired * * Returns an app configuration array * * @param bool $extraMediaTypes * * @return array <string,null|array> */ private function getConfig($extraMediaTypes = false) { $features = $this->configService->getFeaturesList(); //$this->logger->debug("Features: {features}", ['features' => $features]); $nativeSvgSupport = $this->isNativeSvgActivated($features); $mediaTypes = $this->configService->getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport); return ['features' => $features, 'mediatypes' => $mediaTypes]; } /** * Determines if the native SVG feature has been activated * * @param array $features * * @return bool */ private function isNativeSvgActivated($features) { $nativeSvgSupport = false; if (!empty($features) && in_array('native_svg', $features)) { $nativeSvgSupport = true; } return $nativeSvgSupport; } } Controller/ConfigPublicController.php 0000604 00000001366 15247115507 0014021 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; /** * Class ConfigPublicController * * Note: Type casting only works if the "@param" parameters are also included in this class as * their not yet inherited * * @package OCA\Gallery\Controller */ class ConfigPublicController extends ConfigController { /** * @PublicPage * * Returns a list of supported features * * @inheritDoc * * @param bool $extramediatypes */ public function get($extramediatypes = false) { return parent::get($extramediatypes); } } Controller/FilesApiController.php 0000604 00000007225 15247115507 0013151 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; use OCP\IRequest; use OCP\IURLGenerator; use OCP\ILogger; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\RedirectResponse; use OCA\Gallery\Http\ImageResponse; use OCA\Gallery\Service\SearchFolderService; use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\SearchMediaService; use OCA\Gallery\Service\DownloadService; use OCA\Gallery\Service\ServiceException; /** * Class FilesApiController * * @package OCA\Gallery\Controller */ class FilesApiController extends ApiController { use Files; use HttpError; /** @var IURLGenerator */ private $urlGenerator; /** * Constructor * * @param string $appName * @param IRequest $request * @param IURLGenerator $urlGenerator * @param SearchFolderService $searchFolderService * @param ConfigService $configService * @param SearchMediaService $searchMediaService * @param DownloadService $downloadService * @param ILogger $logger */ public function __construct( $appName, IRequest $request, IURLGenerator $urlGenerator, SearchFolderService $searchFolderService, ConfigService $configService, SearchMediaService $searchMediaService, DownloadService $downloadService, ILogger $logger ) { parent::__construct($appName, $request); $this->urlGenerator = $urlGenerator; $this->searchFolderService = $searchFolderService; $this->configService = $configService; $this->searchMediaService = $searchMediaService; $this->downloadService = $downloadService; $this->logger = $logger; } /** * @NoAdminRequired * @NoCSRFRequired * @CORS * * Returns a list of all media files available to the authenticated user * * @see FilesController::getList() * * @param string $location a path representing the current album in the app * @param string $features the list of supported features * @param string $etag the last known etag in the client * @param string $mediatypes the list of supported media types * * @return array <string,array<string,string|int>>|Http\JSONResponse */ public function getList($location, $features, $etag, $mediatypes) { $featuresArray = explode(';', $features); $mediaTypesArray = explode(';', $mediatypes); try { return $this->getFilesAndAlbums($location, $featuresArray, $etag, $mediaTypesArray); } catch (\Exception $exception) { return $this->jsonError($exception, $this->request, $this->logger); } } /** * @NoAdminRequired * @NoCSRFRequired * @CORS * * Sends the file matching the fileId * * In case of error we send an HTML error page * We need to keep the session open in order to be able to send the error message to the error * page * * @param int $fileId the ID of the file we want to download * @param string|null $filename * * @return ImageResponse */ public function download($fileId, $filename = null) { try { $download = $this->getDownload($fileId, $filename); } catch (ServiceException $exception) { $code = $this->getHttpStatusCode($exception); $url = $this->urlGenerator->linkToRoute( $this->appName . '.page.error_page', ['code' => $code] ); // Don't set a cookie for the error message, we don't want it in the API return new RedirectResponse($url); } // That's the only exception out of all the image media types if ($download['mimetype'] === 'image/svg+xml') { $download['mimetype'] = 'text/plain'; } return new ImageResponse($download); } } Controller/PreviewController.php 0000604 00000005647 15247115507 0013104 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Files_Versions\Controller; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IMimeTypeDetector; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\IPreview; use OCP\IRequest; class PreviewController extends Controller { /** @var IRootFolder */ private $rootFolder; /** @var string */ private $userId; /** @var IMimeTypeDetector */ private $mimeTypeDetector; /** @var IPreview */ private $previewManager; public function __construct($appName, IRequest $request, IRootFolder $rootFolder, $userId, IMimeTypeDetector $mimeTypeDetector, IPreview $previewManager) { parent::__construct($appName, $request); $this->rootFolder = $rootFolder; $this->userId = $userId; $this->mimeTypeDetector = $mimeTypeDetector; $this->previewManager = $previewManager; } /** * @NoAdminRequired * @NoCSRFRequired * * @param string $file * @param int $x * @param int $y * @param string $version * @return DataResponse|FileDisplayResponse */ public function getPreview( $file = '', $x = 44, $y = 44, $version = '' ) { if($file === '' || $version === '' || $x === 0 || $y === 0) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } try { $userFolder = $this->rootFolder->getUserFolder($this->userId); /** @var Folder $versionFolder */ $versionFolder = $userFolder->getParent()->get('files_versions'); $mimeType = $this->mimeTypeDetector->detectPath($file); $file = $versionFolder->get($file.'.v'.$version); /** @var File $file */ $f = $this->previewManager->getPreview($file, $x, $y, true, IPreview::MODE_FILL, $mimeType); return new FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]); } catch (NotFoundException $e) { return new DataResponse([], Http::STATUS_NOT_FOUND); } catch (\InvalidArgumentException $e) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } } } Controller/Preview.php 0000604 00000012567 15247115507 0011037 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * * @copyright Olivier Paroz 2017 * @copyright Robin Appelman 2017 */ namespace OCA\Gallery\Controller; use OCP\IURLGenerator; use OCP\ILogger; use OCP\Files\File; use OCP\AppFramework\Http; use OCA\Gallery\Service\ServiceException; use OCA\Gallery\Service\NotFoundServiceException; use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\ThumbnailService; use OCA\Gallery\Service\PreviewService; use OCA\Gallery\Service\DownloadService; /** * Class Preview * * @package OCA\Gallery\Controller */ trait Preview { use HttpError; /** @var IURLGenerator */ private $urlGenerator; /** @var ConfigService */ private $configService; /** @var ThumbnailService */ private $thumbnailService; /** @var PreviewService */ private $previewService; /** @var DownloadService */ private $downloadService; /** @var ILogger */ private $logger; /** @type bool */ private $download = false; /** * Exits the controller in a live environment and throws an exception when testing */ protected function exitController() { if (defined('PHPUNIT_RUN')) { throw new \Exception(); // @codeCoverageIgnoreStart } else { exit(); } // @codeCoverageIgnoreEnd } /** * Retrieves the thumbnail to send back to the browser * * The thumbnail is either a resized preview of the file or the original file * Thumbnails are base64encoded before getting sent back * * * @param int $fileId the ID of the file of which we need a thumbnail preview of * @param bool $square whether the thumbnail should be square * @param double $scale whether we're allowed to scale the preview up * * @return array<string,array|string> */ private function getThumbnail($fileId, $square, $scale) { list($width, $height, $aspect, $animatedPreview, $base64Encode) = $this->thumbnailService->getThumbnailSpecs($square, $scale); /** @type File $file */ list($file, $preview, $status) = $this->getData( $fileId, $width, $height, $aspect, $animatedPreview, $base64Encode ); if ($preview === null) { $preview = $this->prepareEmptyThumbnail($file, $status); } return [$preview, $status]; } /** * Returns either a generated preview, the file as-is or an empty object * * @param int $fileId * @param int $width * @param int $height * @param bool $keepAspect * @param bool $animatedPreview * @param bool $base64Encode * * @return array<string,\OC_Image|string> * * @throws NotFoundServiceException */ private function getData( $fileId, $width, $height, $keepAspect = true, $animatedPreview = true, $base64Encode = false ) { /** @type File $file */ list($file, $status) = $this->getFile($fileId); try { if (!is_null($file)) { $data = $this->getPreviewData( $file, $animatedPreview, $width, $height, $keepAspect, $base64Encode ); } else { $data = $this->getErrorData($status); } } catch (ServiceException $exception) { $data = $this->getExceptionData($exception); } array_unshift($data, $file); return $data; } /** * Returns the file of which a preview will be generated * * @param int $fileId * * @return array<File|int|null> */ private function getFile($fileId) { $status = Http::STATUS_OK; try { /** @type File $file */ $file = $this->previewService->getFile($fileId); $this->configService->validateMimeType($file->getMimeType()); } catch (ServiceException $exception) { $file = null; $status = $this->getHttpStatusCode($exception); } return [$file, $status]; } /** * @param File $file * @param bool $animatedPreview * @param int $width * @param int $height * @param bool $keepAspect * @param bool $base64Encode * * @return array<\OC_Image|string, int> */ private function getPreviewData( $file, $animatedPreview, $width, $height, $keepAspect, $base64Encode ) { $status = Http::STATUS_OK; if ($this->previewService->isPreviewRequired($file, $animatedPreview)) { $preview = $this->previewService->createPreview( $file, $width, $height, $keepAspect, $base64Encode ); } else { $preview = $this->downloadService->downloadFile($file, $base64Encode); } if (!$preview) { list($preview, $status) = $this->getErrorData(); } return [$preview, $status]; } /** * Returns an error array * * @param $status * * @return array<null|int> */ private function getErrorData($status = Http::STATUS_INTERNAL_SERVER_ERROR) { return [null, $status]; } /** * Returns an error array * * @param ServiceException $exception * * @return array<null|int|string> */ private function getExceptionData($exception) { $code = $this->getHttpStatusCode($exception); return $this->getErrorData($code); } /** * Prepares an empty Thumbnail array to send back * * When we can't even get the file information, we send an empty mimeType * * @param File $file * @param int $status * * @return array<string,null|string> */ private function prepareEmptyThumbnail($file, $status) { $thumbnail = []; if ($status !== Http::STATUS_NOT_FOUND) { $mimeType = ''; if ($file) { $mimeType = $file->getMimeType(); } $thumbnail = ['preview' => null, 'mimetype' => $mimeType]; } return $thumbnail; } } Controller/Files.php 0000604 00000010412 15247115507 0010443 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * * @copyright Olivier Paroz 2017 * @copyright Robin Appelman 2017 */ namespace OCA\Gallery\Controller; use OCP\Files\File; use OCP\Files\Folder; use OCP\ILogger; use OCP\AppFramework\Http; use OCA\Gallery\Service\SearchFolderService; use OCA\Gallery\Service\ConfigService; use OCA\Gallery\Service\SearchMediaService; use OCA\Gallery\Service\DownloadService; /** * Trait Files * * @package OCA\Gallery\Controller */ trait Files { use PathManipulation; /** @var SearchFolderService */ private $searchFolderService; /** @var ConfigService */ private $configService; /** @var SearchMediaService */ private $searchMediaService; /** @var DownloadService */ private $downloadService; /** @var ILogger */ private $logger; /** * @NoAdminRequired * * Returns a list of all media files and albums available to the authenticated user * * * Authentication can be via a login/password or a token/(password) * * For private galleries, it returns all media files, with the full path from the root * folder For public galleries, the path starts from the folder the link gives access to * (virtual root) * * An exception is only caught in case something really wrong happens. As we don't test * files before including them in the list, we may return some bad apples * * @param string $location a path representing the current album in the app * @param array $features the list of supported features * @param string $etag the last known etag in the client * @param array $mediatypes the list of supported media types * * @return array <string,array<string,string|int>>|Http\JSONResponse */ private function getFilesAndAlbums($location, $features, $etag, $mediatypes) { $files = []; $albums = []; $updated = true; /** @var Folder $folderNode */ list($folderPathFromRoot, $folderNode) = $this->searchFolderService->getCurrentFolder(rawurldecode($location), $features); $albumConfig = $this->configService->getConfig($folderNode, $features); if ($folderNode->getEtag() !== $etag) { list($files, $albums) = $this->searchMediaService->getMediaFiles( $folderNode, $mediatypes, $features ); } else { $updated = false; } $files = $this->fixPaths($files, $folderPathFromRoot); return $this->formatResults($files, $albums, $albumConfig, $folderPathFromRoot, $updated); } /** * Generates shortened paths to the media files * * We only want to keep one folder between the current folder and the found media file * /root/folder/sub1/sub2/file.ext * becomes * /root/folder/file.ext * * @param array $files * @param string $folderPathFromRoot * * @return array */ private function fixPaths($files, $folderPathFromRoot) { if (!empty($files)) { foreach ($files as &$file) { $file['path'] = $this->getReducedPath($file['path'], $folderPathFromRoot); } } return $files; } /** * Simply builds and returns an array containing the list of files, the album information and * whether the location has changed or not * * @param array $files * @param array $albums * @param array $albumConfig * @param string $folderPathFromRoot * @param bool $updated * * @return array * @internal param $array <string,string|int> $files */ private function formatResults($files, $albums, $albumConfig, $folderPathFromRoot, $updated) { return [ 'files' => $files, 'albums' => $albums, 'albumconfig' => $albumConfig, 'albumpath' => $folderPathFromRoot, 'updated' => $updated ]; } /** * Generates the download data * * @param int $fileId the ID of the file of which we need a large preview of * @param string|null $filename * * @return array|false */ private function getDownload($fileId, $filename) { /** @type File $file */ $file = $this->downloadService->getFile($fileId); $this->configService->validateMimeType($file->getMimeType()); $download = $this->downloadService->downloadFile($file); if (is_null($filename)) { $filename = $file->getName(); } $download['name'] = $filename; return $download; } } Controller/PreviewPublicController.php 0000604 00000002721 15247115507 0014231 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; /** * Class PreviewPublicController * * Note: Type casting only works if the "@param" parameters are also included in this class as * their not yet inherited * * @package OCA\Gallery\Controller */ class PreviewPublicController extends PreviewController { /** * @PublicPage * @UseSession * * Generates thumbnails for public galleries * * The session needs to be maintained open or previews can't be generated * for files located on encrypted storage * * @inheritDoc * * @param string $ids the ID of the files of which we need thumbnail previews of * @param bool $square * @param float $scale */ public function getThumbnails($ids, $square, $scale) { return parent::getThumbnails($ids, $square, $scale); } /** * @PublicPage * @UseSession * * Shows a large preview of a file * * The session needs to be maintained open or previews can't be generated * for files located on encrypted storage * * @inheritDoc * * @param int $fileId the ID of the file of which we need a large preview of * @param int $width * @param int $height */ public function getPreview($fileId, $width, $height) { return parent::getPreview($fileId, $width, $height); } } Controller/FilesPublicController.php 0000604 00000002536 15247115507 0013656 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; /** * Class FilesPublicController * * Note: Type casting only works if the "@param" parameters are also included in this class as * their not yet inherited * * @package OCA\Gallery\Controller */ class FilesPublicController extends FilesController { /** * @PublicPage * * Returns a list of all images from the folder the link gives access to * * @inheritDoc * * @param string $location a path representing the current album in the app * @param string $features the list of supported features * @param string $etag the last known etag in the client * @param string $mediatypes the list of supported media types */ public function getList($location, $features, $etag, $mediatypes) { return parent::getList($location, $features, $etag, $mediatypes); } /** * @PublicPage * @NoCSRFRequired * * Sends the file matching the fileId * * @inheritDoc * * @param int $fileId the ID of the file we want to download * @param string|null $filename */ public function download($fileId, $filename = null) { return parent::download($fileId, $filename); } } Controller/PathManipulation.php 0000604 00000002367 15247115507 0012670 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Controller; /** * @package OCA\Gallery\Controller */ trait PathManipulation { /** * Returns a shortened path for the gallery view * * We only want to keep one folder between the current folder and the found media file * /root/folder/sub1/sub2/file.ext * becomes * /root/folder/file.ext * * @param string $path the full path to a file, which never starts with a slash * @param string $currFolderPath the current folder, which never starts with a slash * * @return string */ private function getReducedPath($path, $currFolderPath) { // Adding a slash to make sure we don't cut a folder in half if ($currFolderPath) { $currFolderPath .= '/'; $relativePath = str_replace($currFolderPath, '', $path); } else { $relativePath = $path; } $subFolders = explode('/', $relativePath); if (count($subFolders) > 2) { $reducedPath = $currFolderPath . $subFolders[0] . '/' . array_pop($subFolders); } else { $reducedPath = $path; } return $reducedPath; } } Config/ConfigException.php 0000604 00000001161 15247115507 0011550 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Config; use OCP\Util; /** * Thrown when the configuration parser cannot parse a file */ class ConfigException extends \Exception { /** * Constructor * * @param string $msg the message contained in the exception */ public function __construct($msg) { Util::writeLog('gallery', 'Exception: ' . $msg, Util::ERROR); parent::__construct($msg); } } Config/ConfigParser.php 0000604 00000016036 15247115507 0011055 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Config; use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Exception\ParseException; use OCP\Files\Folder; use OCP\Files\File; /** * Parses configuration files * * @package OCA\Gallery\Config */ class ConfigParser { /** @var ConfigValidator */ private $configValidator; /** * Constructor */ public function __construct() { $this->configValidator = new ConfigValidator(); } /** * Returns a parsed global configuration if one was found in the root folder * * @param Folder $folder the current folder * @param string $configName name of the configuration file * * @return null|array */ public function getFeaturesList($folder, $configName) { $featuresList = []; $parsedConfig = $this->parseConfig($folder, $configName); $key = 'features'; if (array_key_exists($key, $parsedConfig)) { $featuresList = $this->parseFeatures($parsedConfig[$key]); } return $featuresList; } /** * Returns a parsed configuration if one was found in the current folder * * @param Folder $folder the current folder * @param string $configName name of the configuration file * @param array $currentConfig the configuration collected so far * @param array <string,bool> $completionStatus determines if we already have all we need for a * config sub-section * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder * * @return array <null|array,array<string,bool>> * @throws ConfigException */ public function getFolderConfig($folder, $configName, $currentConfig, $completionStatus, $level ) { $parsedConfig = $this->parseConfig($folder, $configName); list($config, $completionStatus) = $this->buildAlbumConfig($currentConfig, $parsedConfig, $completionStatus, $level); return [$config, $completionStatus]; } /** * Returns a parsed configuration * * @param Folder $folder the current folder * @param string $configName * * @return array * * @throws ConfigException */ private function parseConfig($folder, $configName) { try { /** @var File $configFile */ $configFile = $folder->get($configName); $rawConfig = $configFile->getContent(); $saneConfig = $this->bomFixer($rawConfig); $yaml = new Parser(); $parsedConfig = $yaml->parse($saneConfig); //\OC::$server->getLogger()->debug("rawConfig : {path}", ['path' => $rawConfig]); return $parsedConfig; } catch (\Exception $exception) { $errorMessage = "Problem while reading or parsing the configuration file"; throw new ConfigException($errorMessage); } } /** * Returns only the features which have been enabled * * @param array <string,string> $featuresList the list of features collected from the * configuration file * * @return array */ private function parseFeatures($featuresList) { $parsedFeatures = $featuresList; if (!empty($parsedFeatures)) { $parsedFeatures = array_keys($featuresList, 'yes'); } return $parsedFeatures; } /** * Removes the BOM from a file * * http://us.php.net/manual/en/function.pack.php#104151 * * @param string $file * * @return string */ private function bomFixer($file) { $bom = pack("CCC", 0xef, 0xbb, 0xbf); if (strncmp($file, $bom, 3) === 0) { $file = substr($file, 3); } return $file; } /** * Returns either the local config or one merged with a config containing sorting information * * @param array $currentConfig the configuration collected so far * @param array $parsedConfig the configuration collected in the current folder * @param array <string,bool> $completionStatus determines if we already have all we need for a * config sub-section * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder * * @return array <null|array,array<string,bool>> */ private function buildAlbumConfig($currentConfig, $parsedConfig, $completionStatus, $level) { foreach ($completionStatus as $key => $complete) { if (!$this->isConfigItemComplete($key, $parsedConfig, $complete)) { $parsedConfigItem = $parsedConfig[$key]; if ($this->isConfigUsable($key, $parsedConfigItem, $level)) { list($configItem, $itemComplete) = $this->addConfigItem($key, $parsedConfigItem, $level); $currentConfig = array_merge($currentConfig, $configItem); $completionStatus[$key] = $itemComplete; } } } return [$currentConfig, $completionStatus]; } /** * Determines if we already have everything we need for this configuration sub-section * * @param string $key the configuration sub-section identifier * @param array $parsedConfig the configuration for that sub-section * @param bool $complete * * @return bool */ private function isConfigItemComplete($key, $parsedConfig, $complete) { return !(!$complete && array_key_exists($key, $parsedConfig) && !empty($parsedConfig[$key])); } /** * Determines if we can use this configuration sub-section * * It's possible in two cases: * * the configuration was collected from the currently opened folder * * the configuration was collected in a parent folder and is inheritable * * We also need to make sure that the values contained in the configuration are safe for web use * * @param string $key the configuration sub-section identifier * @param array $parsedConfigItem the configuration for a sub-section * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder * * @return bool */ private function isConfigUsable($key, $parsedConfigItem, $level) { $inherit = $this->isConfigInheritable($parsedConfigItem); $usable = $level === 0 || $inherit; $safe = $this->configValidator->isConfigSafe($key, $parsedConfigItem); return $usable && $safe; } /** * Adds a config sub-section to the global config * * @param string $key the configuration sub-section identifier * @param array $parsedConfigItem the configuration for a sub-section * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder * * @return array<null|array<string,string>,bool> */ private function addConfigItem($key, $parsedConfigItem, $level) { if ($key === 'sorting' && !array_key_exists('type', $parsedConfigItem)) { return [[], false]; } else { $parsedConfigItem['level'] = $level; $configItem = [$key => $parsedConfigItem]; $itemComplete = true; return [$configItem, $itemComplete]; } } /** * Determines if we can use a configuration sub-section found in parent folders * * @param array $parsedConfigItem the configuration for a sub-section * * @return bool */ private function isConfigInheritable($parsedConfigItem) { $inherit = false; if (array_key_exists('inherit', $parsedConfigItem)) { $inherit = $parsedConfigItem['inherit']; } if ($inherit === 'yes') { $inherit = true; } return $inherit; } } Config/ConfigValidator.php 0000604 00000004747 15247115507 0011554 0 ustar 00 <?php /** * Nextcloud - Gallery * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz <galleryapps@oparoz.com> * * @copyright Olivier Paroz 2017 */ namespace OCA\Gallery\Config; /** * Validates parsed configuration elements * * @package OCA\Gallery\Config */ class ConfigValidator { /** * Determines if the content of that sub-section is safe for web use * * @param string $key the configuration sub-section identifier * @param array $parsedConfigItem the configuration for a sub-section * * @return bool */ public function isConfigSafe($key, $parsedConfigItem) { $safe = true; switch ($key) { case 'sorting': $safe = $this->isSortingSafe('type',$parsedConfigItem, $safe); $safe = $this->isSortingSafe('order',$parsedConfigItem, $safe); break; case 'design': $safe = $this->isDesignColourSafe($parsedConfigItem, $safe); break; } return $safe; } /** * Determines if the sorting type found in the config file is safe for web use * @param string will specify the key to check 'type' or 'order' * @param array $parsedConfigItem the sorting configuration to analyse * @param bool $safe whether the current config has been deemed safe to use so far * @return bool */ private function isSortingSafe($key,$parsedConfigItem, $safe) { if ($safe && array_key_exists($key, $parsedConfigItem)) { $safe = $safe && $this->sortingValidator($key, $parsedConfigItem[ $key ]); } return $safe; } /** * Determines if the background colour found in the config file is safe for web use * * @param array $parsedConfigItem the design configuration to analyse * @param bool $safe whether the current config has been deemed safe to use so far * * @return bool */ private function isDesignColourSafe($parsedConfigItem, $safe) { if (array_key_exists('background', $parsedConfigItem)) { $background = $parsedConfigItem['background']; $safe = $safe && ctype_xdigit(substr($background, 1)); } return $safe; } /** * Validates the parsed sorting values against allowed values * * @param string $section the section in the sorting config to be analysed * @param string $value the value found in that section * * @return bool */ private function sortingValidator($section, $value) { if ($section === 'type') { $validValues = ['date', 'name']; } else { $validValues = ['des', 'asc']; } return in_array($value, $validValues); } } Expiration.php 0000604 00000012647 15247115744 0007417 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files_Versions; use \OCP\IConfig; use \OCP\AppFramework\Utility\ITimeFactory; class Expiration { // how long do we keep files a version if no other value is defined in the config file (unit: days) const NO_OBLIGATION = -1; /** @var ITimeFactory */ private $timeFactory; /** @var string */ private $retentionObligation; /** @var int */ private $minAge; /** @var int */ private $maxAge; /** @var bool */ private $canPurgeToSaveSpace; public function __construct(IConfig $config,ITimeFactory $timeFactory){ $this->timeFactory = $timeFactory; $this->retentionObligation = $config->getSystemValue('versions_retention_obligation', 'auto'); if ($this->retentionObligation !== 'disabled') { $this->parseRetentionObligation(); } } /** * Is versions expiration enabled * @return bool */ public function isEnabled(){ return $this->retentionObligation !== 'disabled'; } /** * Is default expiration active */ public function shouldAutoExpire(){ return $this->minAge === self::NO_OBLIGATION || $this->maxAge === self::NO_OBLIGATION; } /** * Check if given timestamp in expiration range * @param int $timestamp * @param bool $quotaExceeded * @return bool */ public function isExpired($timestamp, $quotaExceeded = false){ // No expiration if disabled if (!$this->isEnabled()) { return false; } // Purge to save space (if allowed) if ($quotaExceeded && $this->canPurgeToSaveSpace) { return true; } $time = $this->timeFactory->getTime(); // Never expire dates in future e.g. misconfiguration or negative time // adjustment if ($time<$timestamp) { return false; } // Purge as too old if ($this->maxAge !== self::NO_OBLIGATION) { $maxTimestamp = $time - ($this->maxAge * 86400); $isOlderThanMax = $timestamp < $maxTimestamp; } else { $isOlderThanMax = false; } if ($this->minAge !== self::NO_OBLIGATION) { // older than Min obligation and we are running out of quota? $minTimestamp = $time - ($this->minAge * 86400); $isMinReached = ($timestamp < $minTimestamp) && $quotaExceeded; } else { $isMinReached = false; } return $isOlderThanMax || $isMinReached; } /** * Get maximal retention obligation as a timestamp * @return int */ public function getMaxAgeAsTimestamp(){ $maxAge = false; if ($this->isEnabled() && $this->maxAge !== self::NO_OBLIGATION) { $time = $this->timeFactory->getTime(); $maxAge = $time - ($this->maxAge * 86400); } return $maxAge; } /** * Read versions_retention_obligation, validate it * and set private members accordingly */ private function parseRetentionObligation(){ $splitValues = explode(',', $this->retentionObligation); if (!isset($splitValues[0])) { $minValue = 'auto'; } else { $minValue = trim($splitValues[0]); } if (!isset($splitValues[1])) { $maxValue = 'auto'; } else { $maxValue = trim($splitValues[1]); } $isValid = true; // Validate if (!ctype_digit($minValue) && $minValue !== 'auto') { $isValid = false; \OC::$server->getLogger()->warning( $minValue . ' is not a valid value for minimal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.', ['app'=>'files_versions'] ); } if (!ctype_digit($maxValue) && $maxValue !== 'auto') { $isValid = false; \OC::$server->getLogger()->warning( $maxValue . ' is not a valid value for maximal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.', ['app'=>'files_versions'] ); } if (!$isValid){ $minValue = 'auto'; $maxValue = 'auto'; } if ($minValue === 'auto' && $maxValue === 'auto') { // Default: Delete anytime if space needed $this->minAge = self::NO_OBLIGATION; $this->maxAge = self::NO_OBLIGATION; $this->canPurgeToSaveSpace = true; } elseif ($minValue !== 'auto' && $maxValue === 'auto') { // Keep for X days but delete anytime if space needed $this->minAge = intval($minValue); $this->maxAge = self::NO_OBLIGATION; $this->canPurgeToSaveSpace = true; } elseif ($minValue === 'auto' && $maxValue !== 'auto') { // Delete anytime if space needed, Delete all older than max automatically $this->minAge = self::NO_OBLIGATION; $this->maxAge = intval($maxValue); $this->canPurgeToSaveSpace = true; } elseif ($minValue !== 'auto' && $maxValue !== 'auto') { // Delete all older than max OR older than min if space needed // Max < Min as per https://github.com/owncloud/core/issues/16301 if ($maxValue < $minValue) { $maxValue = $minValue; } $this->minAge = intval($minValue); $this->maxAge = intval($maxValue); $this->canPurgeToSaveSpace = false; } } } BackgroundJob/ExpireVersions.php 0000604 00000004565 15247115744 0012774 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files_Versions\BackgroundJob; use OCP\IUser; use OCP\IUserManager; use OCA\Files_Versions\AppInfo\Application; use OCA\Files_Versions\Storage; use OCA\Files_Versions\Expiration; class ExpireVersions extends \OC\BackgroundJob\TimedJob { const ITEMS_PER_SESSION = 1000; /** * @var Expiration */ private $expiration; /** * @var IUserManager */ private $userManager; public function __construct(IUserManager $userManager = null, Expiration $expiration = null) { // Run once per 30 minutes $this->setInterval(60 * 30); if (is_null($expiration) || is_null($userManager)) { $this->fixDIForJobs(); } else { $this->expiration = $expiration; $this->userManager = $userManager; } } protected function fixDIForJobs() { $application = new Application(); $this->expiration = $application->getContainer()->query('Expiration'); $this->userManager = \OC::$server->getUserManager(); } protected function run($argument) { $maxAge = $this->expiration->getMaxAgeAsTimestamp(); if (!$maxAge) { return; } $this->userManager->callForSeenUsers(function(IUser $user) { $uid = $user->getUID(); if (!$this->setupFS($uid)) { return; } Storage::expireOlderThanMaxForUser($uid); }); } /** * Act on behalf on trash item owner * @param string $user * @return boolean */ protected function setupFS($user) { \OC_Util::tearDownFS(); \OC_Util::setupFS($user); // Check if this user has a versions directory $view = new \OC\Files\View('/' . $user); if (!$view->is_dir('/files_versions')) { return false; } return true; } } Storage.php 0000604 00000070127 15247115744 0006676 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Björn Schießle <bjoern@schiessle.org> * @author Carlos Damken <carlos@damken.com> * @author Felix Moeller <mail@felixmoeller.de> * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Versions * * A class to handle the versioning of files. */ namespace OCA\Files_Versions; use OC\Files\Filesystem; use OC\Files\View; use OCA\Files_Versions\AppInfo\Application; use OCA\Files_Versions\Command\Expire; use OCP\Files\NotFoundException; use OCP\Lock\ILockingProvider; use OCP\User; class Storage { const DEFAULTENABLED=true; const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota const VERSIONS_ROOT = 'files_versions/'; const DELETE_TRIGGER_MASTER_REMOVED = 0; const DELETE_TRIGGER_RETENTION_CONSTRAINT = 1; const DELETE_TRIGGER_QUOTA_EXCEEDED = 2; // files for which we can remove the versions after the delete operation was successful private static $deletedFiles = array(); private static $sourcePathAndUser = array(); private static $max_versions_per_interval = array( //first 10sec, one version every 2sec 1 => array('intervalEndsAfter' => 10, 'step' => 2), //next minute, one version every 10sec 2 => array('intervalEndsAfter' => 60, 'step' => 10), //next hour, one version every minute 3 => array('intervalEndsAfter' => 3600, 'step' => 60), //next 24h, one version every hour 4 => array('intervalEndsAfter' => 86400, 'step' => 3600), //next 30days, one version per day 5 => array('intervalEndsAfter' => 2592000, 'step' => 86400), //until the end one version per week 6 => array('intervalEndsAfter' => -1, 'step' => 604800), ); /** @var \OCA\Files_Versions\AppInfo\Application */ private static $application; /** * get the UID of the owner of the file and the path to the file relative to * owners files folder * * @param string $filename * @return array * @throws \OC\User\NoUserException */ public static function getUidAndFilename($filename) { $uid = Filesystem::getOwner($filename); $userManager = \OC::$server->getUserManager(); // if the user with the UID doesn't exists, e.g. because the UID points // to a remote user with a federated cloud ID we use the current logged-in // user. We need a valid local user to create the versions if (!$userManager->userExists($uid)) { $uid = User::getUser(); } Filesystem::initMountPoints($uid); if ( $uid != User::getUser() ) { $info = Filesystem::getFileInfo($filename); $ownerView = new View('/'.$uid.'/files'); try { $filename = $ownerView->getPath($info['fileid']); // make sure that the file name doesn't end with a trailing slash // can for example happen single files shared across servers $filename = rtrim($filename, '/'); } catch (NotFoundException $e) { $filename = null; } } return [$uid, $filename]; } /** * Remember the owner and the owner path of the source file * * @param string $source source path */ public static function setSourcePathAndUser($source) { list($uid, $path) = self::getUidAndFilename($source); self::$sourcePathAndUser[$source] = array('uid' => $uid, 'path' => $path); } /** * Gets the owner and the owner path from the source path * * @param string $source source path * @return array with user id and path */ public static function getSourcePathAndUser($source) { if (isset(self::$sourcePathAndUser[$source])) { $uid = self::$sourcePathAndUser[$source]['uid']; $path = self::$sourcePathAndUser[$source]['path']; unset(self::$sourcePathAndUser[$source]); } else { $uid = $path = false; } return array($uid, $path); } /** * get current size of all versions from a given user * * @param string $user user who owns the versions * @return int versions size */ private static function getVersionsSize($user) { $view = new View('/' . $user); $fileInfo = $view->getFileInfo('/files_versions'); return isset($fileInfo['size']) ? $fileInfo['size'] : 0; } /** * store a new version of a file. */ public static function store($filename) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { // if the file gets streamed we need to remove the .part extension // to get the right target $ext = pathinfo($filename, PATHINFO_EXTENSION); if ($ext === 'part') { $filename = substr($filename, 0, strlen($filename) - 5); } // we only handle existing files if (! Filesystem::file_exists($filename) || Filesystem::is_dir($filename)) { return false; } list($uid, $filename) = self::getUidAndFilename($filename); $files_view = new View('/'.$uid .'/files'); $users_view = new View('/'.$uid); // no use making versions for empty files if ($files_view->filesize($filename) === 0) { return false; } // create all parent folders self::createMissingDirectories($filename, $users_view); self::scheduleExpire($uid, $filename); // store a new version of a file $mtime = $users_view->filemtime('files/' . $filename); $users_view->copy('files/' . $filename, 'files_versions/' . $filename . '.v' . $mtime); // call getFileInfo to enforce a file cache entry for the new version $users_view->getFileInfo('files_versions/' . $filename . '.v' . $mtime); } } /** * mark file as deleted so that we can remove the versions if the file is gone * @param string $path */ public static function markDeletedFile($path) { list($uid, $filename) = self::getUidAndFilename($path); self::$deletedFiles[$path] = array( 'uid' => $uid, 'filename' => $filename); } /** * delete the version from the storage and cache * * @param View $view * @param string $path */ protected static function deleteVersion($view, $path) { $view->unlink($path); /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ list($storage, $internalPath) = $view->resolvePath($path); $cache = $storage->getCache($internalPath); $cache->remove($internalPath); } /** * Delete versions of a file */ public static function delete($path) { $deletedFile = self::$deletedFiles[$path]; $uid = $deletedFile['uid']; $filename = $deletedFile['filename']; if (!Filesystem::file_exists($path)) { $view = new View('/' . $uid . '/files_versions'); $versions = self::getVersions($uid, $filename); if (!empty($versions)) { foreach ($versions as $v) { \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED)); self::deleteVersion($view, $filename . '.v' . $v['version']); \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path . $v['version'], 'trigger' => self::DELETE_TRIGGER_MASTER_REMOVED)); } } } unset(self::$deletedFiles[$path]); } /** * Rename or copy versions of a file of the given paths * * @param string $sourcePath source path of the file to move, relative to * the currently logged in user's "files" folder * @param string $targetPath target path of the file to move, relative to * the currently logged in user's "files" folder * @param string $operation can be 'copy' or 'rename' */ public static function renameOrCopy($sourcePath, $targetPath, $operation) { list($sourceOwner, $sourcePath) = self::getSourcePathAndUser($sourcePath); // it was a upload of a existing file if no old path exists // in this case the pre-hook already called the store method and we can // stop here if ($sourcePath === false) { return true; } list($targetOwner, $targetPath) = self::getUidAndFilename($targetPath); $sourcePath = ltrim($sourcePath, '/'); $targetPath = ltrim($targetPath, '/'); $rootView = new View(''); // did we move a directory ? if ($rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) { // does the directory exists for versions too ? if ($rootView->is_dir('/' . $sourceOwner . '/files_versions/' . $sourcePath)) { // create missing dirs if necessary self::createMissingDirectories($targetPath, new View('/'. $targetOwner)); // move the directory containing the versions $rootView->$operation( '/' . $sourceOwner . '/files_versions/' . $sourcePath, '/' . $targetOwner . '/files_versions/' . $targetPath ); } } else if ($versions = Storage::getVersions($sourceOwner, '/' . $sourcePath)) { // create missing dirs if necessary self::createMissingDirectories($targetPath, new View('/'. $targetOwner)); foreach ($versions as $v) { // move each version one by one to the target directory $rootView->$operation( '/' . $sourceOwner . '/files_versions/' . $sourcePath.'.v' . $v['version'], '/' . $targetOwner . '/files_versions/' . $targetPath.'.v'.$v['version'] ); } } // if we moved versions directly for a file, schedule expiration check for that file if (!$rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) { self::scheduleExpire($targetOwner, $targetPath); } } /** * Rollback to an old version of a file. * * @param string $file file name * @param int $revision revision timestamp * @return bool */ public static function rollback($file, $revision) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { // add expected leading slash $file = '/' . ltrim($file, '/'); list($uid, $filename) = self::getUidAndFilename($file); if ($uid === null || trim($filename, '/') === '') { return false; } $users_view = new View('/'.$uid); $files_view = new View('/'. User::getUser().'/files'); $versionCreated = false; $fileInfo = $files_view->getFileInfo($file); // check if user has the permissions to revert a version if (!$fileInfo->isUpdateable()) { return false; } //first create a new version $version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename); if (!$users_view->file_exists($version)) { $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename)); $versionCreated = true; } $fileToRestore = 'files_versions' . $filename . '.v' . $revision; // Restore encrypted version of the old file for the newly restored file // This has to happen manually here since the file is manually copied below $oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion(); $oldFileInfo = $users_view->getFileInfo($fileToRestore); $cache = $fileInfo->getStorage()->getCache(); $cache->update( $fileInfo->getId(), [ 'encrypted' => $oldVersion, 'encryptedVersion' => $oldVersion, 'size' => $oldFileInfo->getSize() ] ); // rollback if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) { $files_view->touch($file, $revision); Storage::scheduleExpire($uid, $file); \OC_Hook::emit('\OCP\Versions', 'rollback', array( 'path' => $filename, 'revision' => $revision, )); return true; } else if ($versionCreated) { self::deleteVersion($users_view, $version); } } return false; } /** * Stream copy file contents from $path1 to $path2 * * @param View $view view to use for copying * @param string $path1 source file to copy * @param string $path2 target file * * @return bool true for success, false otherwise */ private static function copyFileContents($view, $path1, $path2) { /** @var \OC\Files\Storage\Storage $storage1 */ list($storage1, $internalPath1) = $view->resolvePath($path1); /** @var \OC\Files\Storage\Storage $storage2 */ list($storage2, $internalPath2) = $view->resolvePath($path2); $view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE); $view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE); // TODO add a proper way of overwriting a file while maintaining file ids if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) { $source = $storage1->fopen($internalPath1, 'r'); $target = $storage2->fopen($internalPath2, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); fclose($source); fclose($target); if ($result !== false) { $storage1->unlink($internalPath1); } } else { $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); } $view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE); $view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE); return ($result !== false); } /** * get a list of all available versions of a file in descending chronological order * @param string $uid user id from the owner of the file * @param string $filename file to find versions of, relative to the user files dir * @param string $userFullPath * @return array versions newest version first */ public static function getVersions($uid, $filename, $userFullPath = '') { $versions = array(); if (empty($filename)) { return $versions; } // fetch for old versions $view = new View('/' . $uid . '/'); $pathinfo = pathinfo($filename); $versionedFile = $pathinfo['basename']; $dir = Filesystem::normalizePath(self::VERSIONS_ROOT . '/' . $pathinfo['dirname']); $dirContent = false; if ($view->is_dir($dir)) { $dirContent = $view->opendir($dir); } if ($dirContent === false) { return $versions; } if (is_resource($dirContent)) { while (($entryName = readdir($dirContent)) !== false) { if (!Filesystem::isIgnoredDir($entryName)) { $pathparts = pathinfo($entryName); $filename = $pathparts['filename']; if ($filename === $versionedFile) { $pathparts = pathinfo($entryName); $timestamp = substr($pathparts['extension'], 1); $filename = $pathparts['filename']; $key = $timestamp . '#' . $filename; $versions[$key]['version'] = $timestamp; $versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($timestamp); if (empty($userFullPath)) { $versions[$key]['preview'] = ''; } else { $versions[$key]['preview'] = \OC::$server->getURLGenerator('files_version.Preview.getPreview', ['file' => $userFullPath, 'version' => $timestamp]); } $versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename); $versions[$key]['name'] = $versionedFile; $versions[$key]['size'] = $view->filesize($dir . '/' . $entryName); $versions[$key]['mimetype'] = \OC::$server->getMimeTypeDetector()->detectPath($versionedFile); } } } closedir($dirContent); } // sort with newest version first krsort($versions); return $versions; } /** * Expire versions that older than max version retention time * @param string $uid */ public static function expireOlderThanMaxForUser($uid){ $expiration = self::getExpiration(); $threshold = $expiration->getMaxAgeAsTimestamp(); $versions = self::getAllVersions($uid); if (!$threshold || !array_key_exists('all', $versions)) { return; } $toDelete = []; foreach (array_reverse($versions['all']) as $key => $version) { if (intval($version['version'])<$threshold) { $toDelete[$key] = $version; } else { //Versions are sorted by time - nothing mo to iterate. break; } } $view = new View('/' . $uid . '/files_versions'); if (!empty($toDelete)) { foreach ($toDelete as $version) { \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT)); self::deleteVersion($view, $version['path'] . '.v' . $version['version']); \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT)); } } } /** * translate a timestamp into a string like "5 days ago" * @param int $timestamp * @return string for example "5 days ago" */ private static function getHumanReadableTimestamp($timestamp) { $diff = time() - $timestamp; if ($diff < 60) { // first minute return $diff . " seconds ago"; } elseif ($diff < 3600) { //first hour return round($diff / 60) . " minutes ago"; } elseif ($diff < 86400) { // first day return round($diff / 3600) . " hours ago"; } elseif ($diff < 604800) { //first week return round($diff / 86400) . " days ago"; } elseif ($diff < 2419200) { //first month return round($diff / 604800) . " weeks ago"; } elseif ($diff < 29030400) { // first year return round($diff / 2419200) . " months ago"; } else { return round($diff / 29030400) . " years ago"; } } /** * returns all stored file versions from a given user * @param string $uid id of the user * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename */ private static function getAllVersions($uid) { $view = new View('/' . $uid . '/'); $dirs = array(self::VERSIONS_ROOT); $versions = array(); while (!empty($dirs)) { $dir = array_pop($dirs); $files = $view->getDirectoryContent($dir); foreach ($files as $file) { $fileData = $file->getData(); $filePath = $dir . '/' . $fileData['name']; if ($file['type'] === 'dir') { array_push($dirs, $filePath); } else { $versionsBegin = strrpos($filePath, '.v'); $relPathStart = strlen(self::VERSIONS_ROOT); $version = substr($filePath, $versionsBegin + 2); $relpath = substr($filePath, $relPathStart, $versionsBegin - $relPathStart); $key = $version . '#' . $relpath; $versions[$key] = array('path' => $relpath, 'timestamp' => $version); } } } // newest version first krsort($versions); $result = array(); foreach ($versions as $key => $value) { $size = $view->filesize(self::VERSIONS_ROOT.'/'.$value['path'].'.v'.$value['timestamp']); $filename = $value['path']; $result['all'][$key]['version'] = $value['timestamp']; $result['all'][$key]['path'] = $filename; $result['all'][$key]['size'] = $size; $result['by_file'][$filename][$key]['version'] = $value['timestamp']; $result['by_file'][$filename][$key]['path'] = $filename; $result['by_file'][$filename][$key]['size'] = $size; } return $result; } /** * get list of files we want to expire * @param array $versions list of versions * @param integer $time * @param bool $quotaExceeded is versions storage limit reached * @return array containing the list of to deleted versions and the size of them */ protected static function getExpireList($time, $versions, $quotaExceeded = false) { $expiration = self::getExpiration(); if ($expiration->shouldAutoExpire()) { list($toDelete, $size) = self::getAutoExpireList($time, $versions); } else { $size = 0; $toDelete = []; // versions we want to delete } foreach ($versions as $key => $version) { if ($expiration->isExpired($version['version'], $quotaExceeded) && !isset($toDelete[$key])) { $size += $version['size']; $toDelete[$key] = $version['path'] . '.v' . $version['version']; } } return [$toDelete, $size]; } /** * get list of files we want to expire * @param array $versions list of versions * @param integer $time * @return array containing the list of to deleted versions and the size of them */ protected static function getAutoExpireList($time, $versions) { $size = 0; $toDelete = array(); // versions we want to delete $interval = 1; $step = Storage::$max_versions_per_interval[$interval]['step']; if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) { $nextInterval = -1; } else { $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter']; } $firstVersion = reset($versions); $firstKey = key($versions); $prevTimestamp = $firstVersion['version']; $nextVersion = $firstVersion['version'] - $step; unset($versions[$firstKey]); foreach ($versions as $key => $version) { $newInterval = true; while ($newInterval) { if ($nextInterval == -1 || $prevTimestamp > $nextInterval) { if ($version['version'] > $nextVersion) { //distance between two version too small, mark to delete $toDelete[$key] = $version['path'] . '.v' . $version['version']; $size += $version['size']; \OCP\Util::writeLog('files_versions', 'Mark to expire '. $version['path'] .' next version should be ' . $nextVersion . " or smaller. (prevTimestamp: " . $prevTimestamp . "; step: " . $step, \OCP\Util::INFO); } else { $nextVersion = $version['version'] - $step; $prevTimestamp = $version['version']; } $newInterval = false; // version checked so we can move to the next one } else { // time to move on to the next interval $interval++; $step = Storage::$max_versions_per_interval[$interval]['step']; $nextVersion = $prevTimestamp - $step; if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) { $nextInterval = -1; } else { $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter']; } $newInterval = true; // we changed the interval -> check same version with new interval } } } return array($toDelete, $size); } /** * Schedule versions expiration for the given file * * @param string $uid owner of the file * @param string $fileName file/folder for which to schedule expiration */ private static function scheduleExpire($uid, $fileName) { // let the admin disable auto expire $expiration = self::getExpiration(); if ($expiration->isEnabled()) { $command = new Expire($uid, $fileName); \OC::$server->getCommandBus()->push($command); } } /** * Expire versions which exceed the quota. * * This will setup the filesystem for the given user but will not * tear it down afterwards. * * @param string $filename path to file to expire * @param string $uid user for which to expire the version * @return bool|int|null */ public static function expire($filename, $uid) { $config = \OC::$server->getConfig(); $expiration = self::getExpiration(); if($config->getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' && $expiration->isEnabled()) { // get available disk space for user $user = \OC::$server->getUserManager()->get($uid); if (is_null($user)) { \OCP\Util::writeLog('files_versions', 'Backends provided no user object for ' . $uid, \OCP\Util::ERROR); throw new \OC\User\NoUserException('Backends provided no user object for ' . $uid); } \OC_Util::setupFS($uid); if (!Filesystem::file_exists($filename)) { return false; } if (empty($filename)) { // file maybe renamed or deleted return false; } $versionsFileview = new View('/'.$uid.'/files_versions'); $softQuota = true; $quota = $user->getQuota(); if ( $quota === null || $quota === 'none' ) { $quota = Filesystem::free_space('/'); $softQuota = false; } else { $quota = \OCP\Util::computerFileSize($quota); } // make sure that we have the current size of the version history $versionsSize = self::getVersionsSize($uid); // calculate available space for version history // subtract size of files and current versions size from quota if ($quota >= 0) { if ($softQuota) { $files_view = new View('/' . $uid . '/files'); $rootInfo = $files_view->getFileInfo('/', false); $free = $quota - $rootInfo['size']; // remaining free space for user if ($free > 0) { $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions } else { $availableSpace = $free - $versionsSize; } } else { $availableSpace = $quota; } } else { $availableSpace = PHP_INT_MAX; } $allVersions = Storage::getVersions($uid, $filename); $time = time(); list($toDelete, $sizeOfDeletedVersions) = self::getExpireList($time, $allVersions, $availableSpace <= 0); $availableSpace = $availableSpace + $sizeOfDeletedVersions; $versionsSize = $versionsSize - $sizeOfDeletedVersions; // if still not enough free space we rearrange the versions from all files if ($availableSpace <= 0) { $result = Storage::getAllVersions($uid); $allVersions = $result['all']; foreach ($result['by_file'] as $versions) { list($toDeleteNew, $size) = self::getExpireList($time, $versions, $availableSpace <= 0); $toDelete = array_merge($toDelete, $toDeleteNew); $sizeOfDeletedVersions += $size; } $availableSpace = $availableSpace + $sizeOfDeletedVersions; $versionsSize = $versionsSize - $sizeOfDeletedVersions; } foreach($toDelete as $key => $path) { \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED)); self::deleteVersion($versionsFileview, $path); \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED)); unset($allVersions[$key]); // update array with the versions we keep \OCP\Util::writeLog('files_versions', "Expire: " . $path, \OCP\Util::INFO); } // Check if enough space is available after versions are rearranged. // If not we delete the oldest versions until we meet the size limit for versions, // but always keep the two latest versions $numOfVersions = count($allVersions) -2 ; $i = 0; // sort oldest first and make sure that we start at the first element ksort($allVersions); reset($allVersions); while ($availableSpace < 0 && $i < $numOfVersions) { $version = current($allVersions); \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED)); self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']); \OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED)); \OCP\Util::writeLog('files_versions', 'running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'] , \OCP\Util::INFO); $versionsSize -= $version['size']; $availableSpace += $version['size']; next($allVersions); $i++; } return $versionsSize; // finally return the new size of the version history } return false; } /** * Create recursively missing directories inside of files_versions * that match the given path to a file. * * @param string $filename $path to a file, relative to the user's * "files" folder * @param View $view view on data/user/ */ private static function createMissingDirectories($filename, $view) { $dirname = Filesystem::normalizePath(dirname($filename)); $dirParts = explode('/', $dirname); $dir = "/files_versions"; foreach ($dirParts as $part) { $dir = $dir . '/' . $part; if (!$view->file_exists($dir)) { $view->mkdir($dir); } } } /** * Static workaround * @return Expiration */ protected static function getExpiration(){ if (is_null(self::$application)) { self::$application = new Application(); } return self::$application->getContainer()->query('Expiration'); } } Hooks.php 0000604 00000005357 15247115744 0006360 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCA\Activity\AppInfo\Application; use OCP\IDBConnection; use OCP\Util; /** * Handles the stream and mail queue of a user when he is being deleted */ class Hooks { /** * Delete remaining activities and emails when a user is deleted * * @param array $params The hook params */ static public function deleteUser($params) { $connection = \OC::$server->getDatabaseConnection(); self::deleteUserStream($params['uid']); self::deleteUserMailQueue($connection, $params['uid']); } /** * Delete all items of the stream * * @param string $user */ static protected function deleteUserStream($user) { // Delete activity entries $app = new Application(); /** @var Data $activityData */ $activityData = $app->getContainer()->query(Data::class); $activityData->deleteActivities(array('affecteduser' => $user)); } /** * Delete all mail queue entries * * @param IDBConnection $connection * @param string $user */ static protected function deleteUserMailQueue(IDBConnection $connection, $user) { // Delete entries from mail queue $queryBuilder = $connection->getQueryBuilder(); $queryBuilder->delete('activity_mq') ->where($queryBuilder->expr()->eq('amq_affecteduser', $queryBuilder->createParameter('user'))) ->setParameter('user', $user); $queryBuilder->execute(); } static public function setDefaultsForUser($params) { $config = \OC::$server->getConfig(); if ($config->getUserValue($params['uid'], 'activity','notify_setting_batchtime', null) !== null) { // Already has settings return; } foreach ($config->getAppKeys('activity') as $key) { if (strpos($key, 'notify_') !== 0) { continue; } $config->setUserValue( $params['uid'], 'activity', $key, $config->getAppValue('activity', $key) ); } } /** * Load additional scripts when the files app is visible */ public static function onLoadFilesAppScripts() { Util::addStyle('activity', 'style'); Util::addScript('activity', 'activity-sidebar'); } } Command/ExpireVersions.php 0000604 00000006412 15247115744 0011631 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2016, ownCloud GmbH. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files_Versions\Command; use OCA\Files_Versions\Expiration; use OCA\Files_Versions\Storage; use OCP\IUser; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class ExpireVersions extends Command { /** * @var Expiration */ private $expiration; /** * @var IUserManager */ private $userManager; /** * @param IUserManager|null $userManager * @param Expiration|null $expiration */ public function __construct(IUserManager $userManager = null, Expiration $expiration = null) { parent::__construct(); $this->userManager = $userManager; $this->expiration = $expiration; } protected function configure() { $this ->setName('versions:expire') ->setDescription('Expires the users file versions') ->addArgument( 'user_id', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'expire file versions of the given user(s), if no user is given file versions for all users will be expired.' ); } protected function execute(InputInterface $input, OutputInterface $output) { $maxAge = $this->expiration->getMaxAgeAsTimestamp(); if (!$maxAge) { $output->writeln("No expiry configured."); return; } $users = $input->getArgument('user_id'); if (!empty($users)) { foreach ($users as $user) { if ($this->userManager->userExists($user)) { $output->writeln("Remove deleted files of <info>$user</info>"); $userObject = $this->userManager->get($user); $this->expireVersionsForUser($userObject); } else { $output->writeln("<error>Unknown user $user</error>"); } } } else { $p = new ProgressBar($output); $p->start(); $this->userManager->callForSeenUsers(function(IUser $user) use ($p) { $p->advance(); $this->expireVersionsForUser($user); }); $p->finish(); $output->writeln(''); } } function expireVersionsForUser(IUser $user) { $uid = $user->getUID(); if (!$this->setupFS($uid)) { return; } Storage::expireOlderThanMaxForUser($uid); } /** * Act on behalf on versions item owner * @param string $user * @return boolean */ protected function setupFS($user) { \OC_Util::tearDownFS(); \OC_Util::setupFS($user); // Check if this user has a version directory $view = new \OC\Files\View('/' . $user); if (!$view->is_dir('/files_versions')) { return false; } return true; } } Command/CleanUp.php 0000604 00000006102 15247115744 0010167 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files_Versions\Command; use OCP\Files\IRootFolder; use OCP\IUserBackend; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class CleanUp extends Command { /** @var IUserManager */ protected $userManager; /** @var IRootFolder */ protected $rootFolder; /** * @param IRootFolder $rootFolder * @param IUserManager $userManager */ function __construct(IRootFolder $rootFolder, IUserManager $userManager) { parent::__construct(); $this->userManager = $userManager; $this->rootFolder = $rootFolder; } protected function configure() { $this ->setName('versions:cleanup') ->setDescription('Delete versions') ->addArgument( 'user_id', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'delete versions of the given user(s), if no user is given all versions will be deleted' ); } protected function execute(InputInterface $input, OutputInterface $output) { $users = $input->getArgument('user_id'); if (!empty($users)) { foreach ($users as $user) { if ($this->userManager->userExists($user)) { $output->writeln("Delete versions of <info>$user</info>"); $this->deleteVersions($user); } else { $output->writeln("<error>Unknown user $user</error>"); } } } else { $output->writeln('Delete all versions'); foreach ($this->userManager->getBackends() as $backend) { $name = get_class($backend); if ($backend instanceof IUserBackend) { $name = $backend->getBackendName(); } $output->writeln("Delete versions for users on backend <info>$name</info>"); $limit = 500; $offset = 0; do { $users = $backend->getUsers('', $limit, $offset); foreach ($users as $user) { $output->writeln(" <info>$user</info>"); $this->deleteVersions($user); } $offset += $limit; } while (count($users) >= $limit); } } } /** * delete versions for the given user * * @param string $user */ protected function deleteVersions($user) { \OC_Util::tearDownFS(); \OC_Util::setupFS($user); if ($this->rootFolder->nodeExists('/' . $user . '/files_versions')) { $this->rootFolder->get('/' . $user . '/files_versions')->delete(); } } } Command/Expire.php 0000604 00000002744 15247115744 0010104 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Files_Versions\Command; use OC\Command\FileAccess; use OCA\Files_Versions\Storage; use OCP\Command\ICommand; class Expire implements ICommand { use FileAccess; /** * @var string */ private $fileName; /** * @var string */ private $user; /** * @param string $user * @param string $fileName */ function __construct($user, $fileName) { $this->user = $user; $this->fileName = $fileName; } public function handle() { $userManager = \OC::$server->getUserManager(); if (!$userManager->userExists($this->user)) { // User has been deleted already return; } Storage::expire($this->fileName, $this->user); } } Settings/AdminSection.php 0000604 00000003657 15247116161 0011445 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Survey_Client\Settings; use OCP\IL10N; use OCP\IURLGenerator; use OCP\Settings\IIconSection; class AdminSection implements IIconSection { /** @var IL10N */ private $l; /** @var IURLGenerator */ private $url; public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * returns the ID of the section. It is supposed to be a lower case string * * @returns string */ public function getID() { return 'survey_client'; } /** * returns the translated name as it should be displayed, e.g. 'LDAP / AD * integration'. Use the L10N service to translate it. * * @return string */ public function getName() { return $this->l->t('Usage survey'); } /** * @return int whether the form should be rather on the top or bottom of * the settings navigation. The sections are arranged in ascending order of * the priority values. It is required to return a value between 0 and 99. */ public function getPriority() { return 80; } /** * {@inheritdoc} */ public function getIcon() { return $this->url->imagePath('survey_client', 'app-dark.svg'); } } Settings/AdminSettings.php 0000604 00000006040 15247116161 0011626 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Survey_Client\Settings; use OCA\Survey_Client\Collector; use OCP\AppFramework\Http\TemplateResponse; use OCP\BackgroundJob\IJobList; use OCP\IConfig; use OCP\IDateTimeFormatter; use OCP\IL10N; use OCP\Settings\ISettings; class AdminSettings implements ISettings { /** @var Collector */ private $collector; /** @var IConfig */ private $config; /** @var IL10N */ private $l; /** @var IDateTimeFormatter */ private $dateTimeFormatter; /** @var IJobList */ private $jobList; /** * Admin constructor. * * @param Collector $collector * @param IConfig $config * @param IL10N $l * @param IDateTimeFormatter $dateTimeFormatter * @param IJobList $jobList */ public function __construct(Collector $collector, IConfig $config, IL10N $l, IDateTimeFormatter $dateTimeFormatter, IJobList $jobList ) { $this->collector = $collector; $this->config = $config; $this->l = $l; $this->dateTimeFormatter = $dateTimeFormatter; $this->jobList = $jobList; } /** * @return TemplateResponse */ public function getForm() { $lastSentReportTime = (int) $this->config->getAppValue('survey_client', 'last_sent', 0); if ($lastSentReportTime === 0) { $lastSentReportDate = $this->l->t('Never'); } else { $lastSentReportDate = $this->dateTimeFormatter->formatDate($lastSentReportTime); } $lastReport = $this->config->getAppValue('survey_client', 'last_report', ''); if ($lastReport !== '') { $lastReport = json_encode(json_decode($lastReport, true), JSON_PRETTY_PRINT); } $parameters = [ 'is_enabled' => $this->jobList->has('OCA\Survey_Client\BackgroundJobs\MonthlyReport', null), 'last_sent' => $lastSentReportDate, 'last_report' => $lastReport, 'categories' => $this->collector->getCategories() ]; return new TemplateResponse('survey_client', 'admin', $parameters); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection() { return 'survey_client'; } /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. */ public function getPriority() { return 50; } } Notifier.php 0000604 00000005416 15247116161 0007042 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client; use OCP\IURLGenerator; use OCP\L10N\IFactory; use OCP\Notification\INotification; use OCP\Notification\INotifier; class Notifier implements INotifier { /** @var IFactory */ protected $l10nFactory; /** @var IURLGenerator */ protected $url; /** * Notifier constructor. * * @param IFactory $l10nFactory * @param IURLGenerator $url */ public function __construct(IFactory $l10nFactory, IURLGenerator $url) { $this->l10nFactory = $l10nFactory; $this->url = $url; } /** * @param INotification $notification * @param string $languageCode The code of the language that should be used to prepare the notification * @return INotification * @throws \InvalidArgumentException When the notification was not prepared by a notifier */ public function prepare(INotification $notification, $languageCode) { if ($notification->getApp() !== 'survey_client') { // Not my app => throw throw new \InvalidArgumentException(); } // Read the language from the notification $l = $this->l10nFactory->get('survey_client', $languageCode); $notification->setParsedSubject($l->t('Help improve Nextcloud')) ->setParsedMessage($l->t('Do you want to help us to improve Nextcloud by providing some anonymized data about your setup and usage? You can disable it at any time in the admin settings again.')) ->setLink($this->url->linkToRoute('settings.AdminSettings.index', ['section' => 'survey_client'])) ->setIcon($this->url->imagePath('survey_client', 'app-dark.svg')); foreach ($notification->getActions() as $action) { if ($action->getLabel() === 'disable') { $action->setParsedLabel((string) $l->t('Not now')) ->setLink($this->url->getAbsoluteURL('ocs/v2.php/apps/survey_client/api/v1/monthly'), 'DELETE'); } else if ($action->getLabel() === 'enable') { $action->setParsedLabel((string) $l->t('Send usage')) ->setLink($this->url->getAbsoluteURL('ocs/v2.php/apps/survey_client/api/v1/monthly'), 'POST'); } $notification->addParsedAction($action); } return $notification; } } Categories/FilesSharing.php 0000604 00000006461 15247116161 0011727 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Categories; use OCP\IDBConnection; use OCP\IL10N; /** * Class FilesSharing * * @package OCA\Survey_Client\Categories */ class FilesSharing implements ICategory { /** @var IDBConnection */ protected $connection; /** @var \OCP\IL10N */ protected $l; /** * @param IDBConnection $connection * @param IL10N $l */ public function __construct(IDBConnection $connection, IL10N $l) { $this->connection = $connection; $this->l = $l; } /** * @return string */ public function getCategory() { return 'files_sharing'; } /** * @return string */ public function getDisplayName() { return (string) $this->l->t('Number of shares <em>(per type and permission setting)</em>'); } /** * @return array (string => string|int) */ public function getData() { $query = $this->connection->getQueryBuilder(); $query->selectAlias($query->createFunction('COUNT(*)'), 'num_entries') ->addSelect(['permissions', 'share_type']) ->from('share') ->addGroupBy('permissions') ->addGroupBy('share_type'); $result = $query->execute(); $data = [ 'num_shares' => $this->countEntries('share'), 'num_shares_user' => $this->countShares(0), 'num_shares_groups' => $this->countShares(1), 'num_shares_link' => $this->countShares(3), 'num_shares_link_no_password' => $this->countShares(3, true), 'num_fed_shares_sent' => $this->countShares(6), 'num_fed_shares_received' => $this->countEntries('share_external'), ]; while ($row = $result->fetch()) { $data['permissions_' . $row['share_type'] . '_' . $row['permissions']] = $row['num_entries']; } $result->closeCursor(); return $data; } /** * @param string $tableName * @return int */ protected function countEntries($tableName) { $query = $this->connection->getQueryBuilder(); $query->selectAlias($query->createFunction('COUNT(*)'), 'num_entries') ->from($tableName); $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); return (int) $row['num_entries']; } /** * @param int $type * @param bool $noShareWith * @return int */ protected function countShares($type, $noShareWith = false) { $query = $this->connection->getQueryBuilder(); $query->selectAlias($query->createFunction('COUNT(*)'), 'num_entries') ->from('share') ->where($query->expr()->eq('share_type', $query->createNamedParameter($type))); if ($noShareWith) { $query->andWhere($query->expr()->isNull('share_with')); } $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); return (int) $row['num_entries']; } } Categories/Apps.php 0000604 00000004241 15247116161 0010246 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Categories; use Doctrine\DBAL\Connection; use OCP\IDBConnection; use OCP\IL10N; /** * Class Apps * * @package OCA\Survey_Client\Categories */ class Apps implements ICategory { /** @var IDBConnection */ protected $connection; /** @var \OCP\IL10N */ protected $l; /** * @param IDBConnection $connection * @param IL10N $l */ public function __construct(IDBConnection $connection, IL10N $l) { $this->connection = $connection; $this->l = $l; } /** * @return string */ public function getCategory() { return 'apps'; } /** * @return string */ public function getDisplayName() { return (string) $this->l->t('App list <em>(for each app: name, version, is enabled?)</em>'); } /** * @return array (string => string|int) */ public function getData() { $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('appconfig') ->where($query->expr()->in('configkey', $query->createNamedParameter( ['enabled', 'installed_version'], Connection::PARAM_STR_ARRAY ))); $result = $query->execute(); $data = []; while ($row = $result->fetch()) { if ($row['configkey'] === 'enabled' && $row['configvalue'] === 'no') { $data[$row['appid']] = 'disabled'; } if ($row['configkey'] === 'installed_version' && !isset($data[$row['appid']])) { $data[$row['appid']] = $row['configvalue']; } } $result->closeCursor(); return $data; } } Categories/Encryption.php 0000604 00000003537 15247116161 0011504 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Categories; use OCP\IConfig; use OCP\IL10N; /** * Class Encryption * * @package OCA\Survey_Client\Categories */ class Encryption implements ICategory { /** @var \OCP\IConfig */ protected $config; /** @var \OCP\IL10N */ protected $l; /** * @param IConfig $config * @param IL10N $l */ public function __construct(IConfig $config, IL10N $l) { $this->config = $config; $this->l = $l; } /** * @return string */ public function getCategory() { return 'encryption'; } /** * @return string */ public function getDisplayName() { return (string) $this->l->t('Encryption information <em>(is it enabled?, what is the default module)</em>'); } /** * @return array (string => string|int) */ public function getData() { $data = [ 'enabled' => $this->config->getAppValue('core', 'encryption_enabled', 'no') === 'yes' ? 'yes' : 'no', 'default_module' => $this->config->getAppValue('core', 'default_encryption_module') === 'OC_DEFAULT_MODULE' ? 'yes' : 'no', ]; if ($data['enabled'] === 'yes') { unset($data['default_module']); } return $data; } } Categories/ICategory.php 0000604 00000002157 15247116161 0011235 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Categories; /** * Interface ICategory * * TODO Move to core public API? * * @package OCA\Survey_Client\Categories */ interface ICategory { /** * @return string */ public function getCategory(); /** * @return string */ public function getDisplayName(); /** * @return array (string => string|int) */ public function getData(); } Categories/Server.php 0000604 00000005107 15247116161 0010613 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Categories; use OCP\IConfig; use OCP\IL10N; /** * Class Server * * @package OCA\Survey_Client\Categories */ class Server implements ICategory { /** @var \OCP\IConfig */ protected $config; /** @var \OCP\IL10N */ protected $l; /** * @param IConfig $config * @param IL10N $l */ public function __construct(IConfig $config, IL10N $l) { $this->config = $config; $this->l = $l; } /** * @return string */ public function getCategory() { return 'server'; } /** * @return string */ public function getDisplayName() { return (string) $this->l->t('Server instance details <em>(version, memcache used, locking/previews/avatars enabled?)</em>'); } /** * @return array (string => string|int) */ public function getData() { return [ 'version' => $this->config->getSystemValue('version'), 'code' => $this->codeLocation(), 'enable_avatars' => $this->config->getSystemValue('enable_avatars', true) ? 'yes' : 'no', 'enable_previews' => $this->config->getSystemValue('enable_previews', true) ? 'yes' : 'no', 'memcache.local' => $this->config->getSystemValue('memcache.local', 'none'), 'memcache.distributed' => $this->config->getSystemValue('memcache.distributed', 'none'), 'asset-pipeline.enabled' => $this->config->getSystemValue('asset-pipeline.enabled') ? 'yes' : 'no', 'filelocking.enabled' => $this->config->getSystemValue('filelocking.enabled', true) ? 'yes' : 'no', 'memcache.locking' => $this->config->getSystemValue('memcache.locking', 'none'), 'debug' => $this->config->getSystemValue('debug', false) ? 'yes' : 'no', 'cron' => $this->config->getAppValue('core', 'backgroundjobs_mode', 'ajax'), ]; } protected function codeLocation() { if (file_exists(\OC::$SERVERROOT . '/.git') && is_dir(\OC::$SERVERROOT . '/.git')) { return 'git'; } return 'other'; } } Categories/Stats.php 0000604 00000007543 15247116161 0010451 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Categories; use OCP\IDBConnection; use OCP\IL10N; /** * Class Stats * * @package OCA\Survey_Client\Categories */ class Stats implements ICategory { /** @var IDBConnection */ protected $connection; /** @var \OCP\IL10N */ protected $l; /** * @param IDBConnection $connection * @param IL10N $l */ public function __construct(IDBConnection $connection, IL10N $l) { $this->connection = $connection; $this->l = $l; } /** * @return string */ public function getCategory() { return 'stats'; } /** * @return string */ public function getDisplayName() { return (string) $this->l->t('Statistic <em>(number of: files, users, storages per type, comments and tags)</em>'); } /** * @return array (string => string|int) */ public function getData() { return [ 'num_files' => $this->countEntries('filecache'), 'num_users' => $this->countUserEntries(), 'num_storages' => $this->countEntries('storages'), 'num_storages_local' => $this->countStorages('local'), 'num_storages_home' => $this->countStorages('home'), 'num_storages_other' => $this->countStorages('other'), 'num_comments' => $this->countEntries('comments'), 'num_comment_markers' => $this->countEntries('comments_read_markers', 'user_id'), 'num_systemtags' => $this->countEntries('systemtag'), 'num_systemtags_mappings' => $this->countEntries('systemtag_object_mapping'), ]; } /** * @return int */ protected function countUserEntries() { $query = $this->connection->getQueryBuilder(); $query->selectAlias($query->createFunction('COUNT(*)'), 'num_entries') ->from('preferences') ->where($query->expr()->eq('configkey', $query->createNamedParameter('lastLogin'))); $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); return (int) $row['num_entries']; } /** * @param string $type * @return int */ protected function countStorages($type) { $query = $this->connection->getQueryBuilder(); $query->selectAlias($query->createFunction('COUNT(*)'), 'num_entries') ->from('storages'); if ($type === 'home') { $query->where($query->expr()->like('id', $query->createNamedParameter('home::%'))); } else if ($type === 'local') { $query->where($query->expr()->like('id', $query->createNamedParameter('local::%'))); } else if ($type === 'other') { $query->where($query->expr()->notLike('id', $query->createNamedParameter('home::%'))); $query->andWhere($query->expr()->notLike('id', $query->createNamedParameter('local::%'))); } $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); return (int) $row['num_entries']; } /** * @param string $tableName * @param string $column * @return int */ protected function countEntries($tableName, $column = '*') { if ($column !== '*') { $column = 'DISTINCT(' . $column . ')'; } $query = $this->connection->getQueryBuilder(); $query->selectAlias($query->createFunction('COUNT(' . $column . ')'), 'num_entries') ->from($tableName); $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); return (int) $row['num_entries']; } } Categories/Database.php 0000604 00000012314 15247116161 0011047 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Categories; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; /** * Class Database * * @package OCA\Survey_Client\Categories */ class Database implements ICategory { /** @var \OCP\IConfig */ protected $config; /** @var \OCP\IDBConnection */ protected $connection; /** @var \OCP\IL10N */ protected $l; /** * @param IConfig $config * @param IDBConnection $connection * @param IL10N $l */ public function __construct(IConfig $config, IDBConnection $connection, IL10N $l) { $this->config = $config; $this->connection = $connection; $this->l = $l; } /** * @return string */ public function getCategory() { return 'database'; } /** * @return string */ public function getDisplayName() { return (string) $this->l->t('Database environment <em>(type, version, database size)</em>'); } /** * @return array (string => string|int) */ public function getData() { return [ 'type' => $this->config->getSystemValue('dbtype'), 'version' => $this->databaseVersion(), 'size' => $this->databaseSize(), ]; } protected function databaseVersion() { switch ($this->config->getSystemValue('dbtype')) { case 'sqlite': case 'sqlite3': $sql = 'SELECT sqlite_version() AS version'; break; case 'oci': $sql = 'SELECT version FROM v$instance'; break; case 'mysql': case 'pgsql': default: $sql = 'SELECT VERSION() AS version'; break; } $result = $this->connection->executeQuery($sql); $row = $result->fetch(); $result->closeCursor(); if ($row) { return $this->cleanVersion($row['version']); } return 'N/A'; } /** * Copy of phpBB's get_database_size() * @link https://github.com/phpbb/phpbb/blob/release-3.1.6/phpBB/includes/functions_admin.php#L2908-L3043 * * @copyright (c) phpBB Limited <https://www.phpbb.com> * @license GNU General Public License, version 2 (GPL-2.0) * * @return int|string */ protected function databaseSize() { $database_size = false; // This code is heavily influenced by a similar routine in phpMyAdmin 2.2.0 switch ($this->config->getSystemValue('dbtype')) { case 'mysql': $db_name = $this->config->getSystemValue('dbname'); $sql = 'SHOW TABLE STATUS FROM `' . $db_name . '`'; $result = $this->connection->executeQuery($sql); $database_size = 0; while ($row = $result->fetch()) { if ((isset($row['Type']) && $row['Type'] !== 'MRG_MyISAM') || (isset($row['Engine']) && ($row['Engine'] === 'MyISAM' || $row['Engine'] === 'InnoDB'))) { $database_size += $row['Data_length'] + $row['Index_length']; } } $result->closeCursor(); break; case 'sqlite': case 'sqlite3': if (file_exists($this->config->getSystemValue('dbhost'))) { $database_size = filesize($this->config->getSystemValue('dbhost')); } else { $params = $this->connection->getParams(); if (file_exists($params['path'])) { $database_size = filesize($params['path']); } } break; case 'pgsql': $sql = "SELECT proname FROM pg_proc WHERE proname = 'pg_database_size'"; $result = $this->connection->executeQuery($sql); $row = $result->fetch(); $result->closeCursor(); if ($row['proname'] === 'pg_database_size') { $database = $this->config->getSystemValue('dbname'); if (strpos($database, '.') !== false) { list($database, ) = explode('.', $database); } $sql = "SELECT oid FROM pg_database WHERE datname = '$database'"; $result = $this->connection->executeQuery($sql); $row = $result->fetch(); $result->closeCursor(); $oid = $row['oid']; $sql = 'SELECT pg_database_size(' . $oid . ') as size'; $result = $this->connection->executeQuery($sql); $row = $result->fetch(); $result->closeCursor(); $database_size = $row['size']; } break; case 'oci': $sql = 'SELECT SUM(bytes) as dbsize FROM user_segments'; $result = $this->connection->executeQuery($sql); $database_size = ($row = $result->fetch()) ? $row['dbsize'] : false; $result->closeCursor(); break; } return ($database_size !== false) ? $database_size : 'N/A'; } /** * Try to strip away additional information * * @param string $version E.g. `5.6.27-0ubuntu0.14.04.1` * @return string `5.6.27` */ protected function cleanVersion($version) { $matches = []; preg_match('/^(\d+)(\.\d+)(\.\d+)/', $version, $matches); if (isset($matches[0])) { return $matches[0]; } return $version; } } Categories/Php.php 0000604 00000004230 15247116161 0010070 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Categories; use bantu\IniGetWrapper\IniGetWrapper; use OCP\IL10N; /** * Class php * * @package OCA\Survey_Client\Categories */ class Php implements ICategory { /** @var IniGetWrapper */ protected $phpIni; /** @var \OCP\IL10N */ protected $l; /** * @param IniGetWrapper $phpIni * @param IL10N $l */ public function __construct(IniGetWrapper $phpIni, IL10N $l) { $this->phpIni = $phpIni; $this->l = $l; } /** * @return string */ public function getCategory() { return 'php'; } /** * @return string */ public function getDisplayName() { return (string) $this->l->t('PHP environment <em>(version, memory limit, max. execution time, max. file size)</em>'); } /** * @return array (string => string|int) */ public function getData() { return [ 'version' => $this->cleanVersion(PHP_VERSION), 'memory_limit' => $this->phpIni->getBytes('memory_limit'), 'max_execution_time' => $this->phpIni->getNumeric('max_execution_time'), 'upload_max_filesize' => $this->phpIni->getBytes('upload_max_filesize'), ]; } /** * Try to strip away additional information * * @param string $version E.g. `5.5.30-1+deb.sury.org~trusty+1` * @return string `5.5.30` */ protected function cleanVersion($version) { $matches = []; preg_match('/^(\d+)(\.\d+)(\.\d+)/', $version, $matches); if (isset($matches[0])) { return $matches[0]; } return $version; } } Controller/EndpointController.php 0000604 00000004445 15247116161 0013233 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\Controller; use OCA\Survey_Client\Collector; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\BackgroundJob\IJobList; use OCP\IRequest; use OCP\Notification\IManager; class EndpointController extends Controller { /** @var Collector */ protected $collector; /** @var IJobList */ protected $jobList; /** @var IManager */ protected $manager; /** * @param string $appName * @param IRequest $request * @param Collector $collector * @param IJobList $jobList * @param IManager $manager */ public function __construct($appName, IRequest $request, Collector $collector, IJobList $jobList, IManager $manager) { parent::__construct($appName, $request); $this->collector = $collector; $this->jobList = $jobList; $this->manager = $manager; } /** * @return \OC_OCS_Result */ public function enableMonthly() { $this->jobList->add('OCA\Survey_Client\BackgroundJobs\MonthlyReport'); $notification = $this->manager->createNotification(); $notification->setApp('survey_client'); $this->manager->markProcessed($notification); return new \OC_OCS_Result(); } /** * @return \OC_OCS_Result */ public function disableMonthly() { $this->jobList->remove('OCA\Survey_Client\BackgroundJobs\MonthlyReport'); $notification = $this->manager->createNotification(); $notification->setApp('survey_client'); $this->manager->markProcessed($notification); return new \OC_OCS_Result(); } /** * @return \OC_OCS_Result */ public function sendReport() { return $this->collector->sendReport(); } } BackgroundJobs/AdminNotification.php 0000604 00000003571 15247116161 0013557 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\BackgroundJobs; use OC\BackgroundJob\QueuedJob; class AdminNotification extends QueuedJob { protected function run($argument) { $manager = \OC::$server->getNotificationManager(); $urlGenerator = \OC::$server->getURLGenerator(); $notification = $manager->createNotification(); $notification->setApp('survey_client') ->setDateTime(new \DateTime()) ->setSubject('updated') ->setObject('dummy', 23); $enableAction = $notification->createAction(); $enableAction->setLabel('enable') ->setLink($urlGenerator->getAbsoluteURL('ocs/v2.php/apps/survey_client/api/v1/monthly'), 'POST') ->setPrimary(true); $notification->addAction($enableAction); $disableAction = $notification->createAction(); $disableAction->setLabel('disable') ->setLink($urlGenerator->getAbsoluteURL('ocs/v2.php/apps/survey_client/api/v1/monthly'), 'DELETE') ->setPrimary(false); $notification->addAction($disableAction); $adminGroup = \OC::$server->getGroupManager()->get('admin'); foreach ($adminGroup->getUsers() as $admin) { $notification->setUser($admin->getUID()); $manager->notify($notification); } } } BackgroundJobs/MonthlyReport.php 0000604 00000002611 15247116161 0013000 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client\BackgroundJobs; use OC\BackgroundJob\TimedJob; use OCA\Survey_Client\AppInfo\Application; class MonthlyReport extends TimedJob { /** * MonthlyReport constructor. */ public function __construct() { // Run all 28 days $this->setInterval(28 * 24 * 60 * 60); } protected function run($argument) { $application = new Application(); /** @var \OCA\Survey_Client\Collector $collector */ $collector = $application->getContainer()->query('OCA\Survey_Client\Collector'); $result = $collector->sendReport(); if (!$result->succeeded()) { \OC::$server->getLogger()->info('Error while sending usage statistic'); } } } Collector.php 0000604 00000010671 15247116161 0007210 0 ustar 00 <?php /** * @author Joas Schilling <coding@schilljs.com> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Survey_Client; use bantu\IniGetWrapper\IniGetWrapper; use OCA\Survey_Client\Categories\Apps; use OCA\Survey_Client\Categories\Database; use OCA\Survey_Client\Categories\Encryption; use OCA\Survey_Client\Categories\FilesSharing; use OCA\Survey_Client\Categories\ICategory; use OCA\Survey_Client\Categories\Php; use OCA\Survey_Client\Categories\Server; use OCA\Survey_Client\Categories\Stats; use OCP\AppFramework\Http; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; class Collector { const SURVEY_SERVER_URL = 'https://surveyserver.nextcloud.com/'; /** @var ICategory[] */ protected $categories; /** @var IClientService */ protected $clientService; /** @var IConfig */ protected $config; /** @var IDBConnection */ protected $connection; /** @var IniGetWrapper */ protected $phpIni; /** @var \OCP\IL10N */ protected $l; /** * Collector constructor. * * @param IClientService $clientService * @param IConfig $config * @param IDBConnection $connection * @param IniGetWrapper $phpIni * @param IL10N $l */ public function __construct(IClientService $clientService, IConfig $config, IDBConnection $connection, IniGetWrapper $phpIni, IL10N $l) { $this->clientService = $clientService; $this->config = $config; $this->connection = $connection; $this->phpIni = $phpIni; $this->l = $l; } protected function registerCategories() { $this->categories[] = new Server( $this->config, $this->l ); $this->categories[] = new php( $this->phpIni, $this->l ); $this->categories[] = new Database( $this->config, $this->connection, $this->l ); $this->categories[] = new Apps( $this->connection, $this->l ); $this->categories[] = new Stats( $this->connection, $this->l ); $this->categories[] = new FilesSharing( $this->connection, $this->l ); $this->categories[] = new Encryption( $this->config, $this->l ); } /** * @return array */ public function getCategories() { $this->registerCategories(); $categories = []; foreach ($this->categories as $category) { $categories[$category->getCategory()] = [ 'displayName' => $category->getDisplayName(), 'enabled' => $this->config->getAppValue('survey_client', $category->getCategory(), 'yes') === 'yes', ]; } return $categories; } /** * @return array */ public function getReport() { $this->registerCategories(); $tuples = []; foreach ($this->categories as $category) { if ($this->config->getAppValue('survey_client', $category->getCategory(), 'yes') === 'yes') { foreach ($category->getData() as $key => $value) { $tuples[] = [ $category->getCategory(), $key, $value ]; } } } return [ 'id' => $this->config->getSystemValue('instanceid'), 'items' => $tuples, ]; } /** * @return \OC_OCS_Result */ public function sendReport() { $report = $this->getReport(); $client = $this->clientService->newClient(); try { $response = $client->post(self::SURVEY_SERVER_URL . 'ocs/v2.php/apps/survey_server/api/v1/survey', [ 'timeout' => 5, 'query' => [ 'data' => json_encode($report), ], ]); } catch (\Exception $e) { return new \OC_OCS_Result( $report, Http::STATUS_INTERNAL_SERVER_ERROR ); } if ($response->getStatusCode() === Http::STATUS_OK) { $this->config->setAppValue('survey_client', 'last_sent', time()); $this->config->setAppValue('survey_client', 'last_report', json_encode($report)); return new \OC_OCS_Result( $report, 100// HTTP::STATUS_OK, TODO: <status>failure</status><statuscode>200</statuscode> ); } return new \OC_OCS_Result( $report, Http::STATUS_INTERNAL_SERVER_ERROR ); } } Controller/Settings.php 0000604 00000021031 15247130324 0011172 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Controller; use OCA\Activity\CurrentUser; use OCA\Activity\UserSettings; use OCP\Activity\IExtension; use OCP\Activity\IManager; use OCP\Activity\ISetting; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; use OCP\IURLGenerator; use OCP\Security\ISecureRandom; class Settings extends Controller { /** @var \OCP\IConfig */ protected $config; /** @var \OCP\Security\ISecureRandom */ protected $random; /** @var \OCP\IURLGenerator */ protected $urlGenerator; /** @var IManager */ protected $manager; /** @var \OCA\Activity\UserSettings */ protected $userSettings; /** @var \OCP\IL10N */ protected $l10n; /** @var string */ protected $user; /** * constructor of the controller * * @param string $appName * @param IRequest $request * @param IConfig $config * @param ISecureRandom $random * @param IURLGenerator $urlGenerator * @param IManager $manager * @param UserSettings $userSettings * @param IL10N $l10n * @param CurrentUser $currentUser */ public function __construct($appName, IRequest $request, IConfig $config, ISecureRandom $random, IURLGenerator $urlGenerator, IManager $manager, UserSettings $userSettings, IL10N $l10n, CurrentUser $currentUser) { parent::__construct($appName, $request); $this->config = $config; $this->random = $random; $this->urlGenerator = $urlGenerator; $this->manager = $manager; $this->userSettings = $userSettings; $this->l10n = $l10n; $this->user = (string) $currentUser->getUID(); } /** * @NoAdminRequired * * @param int $notify_setting_batchtime * @param bool $notify_setting_self * @param bool $notify_setting_selfemail * @return DataResponse */ public function personal( $notify_setting_batchtime = UserSettings::EMAIL_SEND_HOURLY, $notify_setting_self = false, $notify_setting_selfemail = false) { $settings = $this->manager->getSettings(); foreach ($settings as $setting) { if ($setting->canChangeStream()) { $this->config->setUserValue( $this->user, 'activity', 'notify_stream_' . $setting->getIdentifier(), (int) $this->request->getParam($setting->getIdentifier() . '_stream', false) ); } if ($setting->canChangeMail()) { $this->config->setUserValue( $this->user, 'activity', 'notify_email_' . $setting->getIdentifier(), (int) $this->request->getParam($setting->getIdentifier() . '_email', false) ); } } $email_batch_time = 3600; if ($notify_setting_batchtime === UserSettings::EMAIL_SEND_DAILY) { $email_batch_time = 3600 * 24; } else if ($notify_setting_batchtime === UserSettings::EMAIL_SEND_WEEKLY) { $email_batch_time = 3600 * 24 * 7; } $this->config->setUserValue( $this->user, 'activity', 'notify_setting_batchtime', $email_batch_time ); $this->config->setUserValue( $this->user, 'activity', 'notify_setting_self', (int) $notify_setting_self ); $this->config->setUserValue( $this->user, 'activity', 'notify_setting_selfemail', (int) $notify_setting_selfemail ); return new DataResponse(array( 'data' => array( 'message' => (string) $this->l10n->t('Your settings have been updated.'), ), )); } /** * @param int $notify_setting_batchtime * @param bool $notify_setting_self * @param bool $notify_setting_selfemail * @return DataResponse */ public function admin( $notify_setting_batchtime = UserSettings::EMAIL_SEND_HOURLY, $notify_setting_self = false, $notify_setting_selfemail = false) { $settings = $this->manager->getSettings(); foreach ($settings as $setting) { if ($setting->canChangeStream()) { $this->config->setAppValue( 'activity', 'notify_stream_' . $setting->getIdentifier(), (int) $this->request->getParam($setting->getIdentifier() . '_stream', false) ); } if ($setting->canChangeMail()) { $this->config->setAppValue( 'activity', 'notify_email_' . $setting->getIdentifier(), (int) $this->request->getParam($setting->getIdentifier() . '_email', false) ); } } $email_batch_time = 3600; if ($notify_setting_batchtime === UserSettings::EMAIL_SEND_DAILY) { $email_batch_time = 3600 * 24; } else if ($notify_setting_batchtime === UserSettings::EMAIL_SEND_WEEKLY) { $email_batch_time = 3600 * 24 * 7; } $this->config->setAppValue( 'activity', 'notify_setting_batchtime', $email_batch_time ); $this->config->setAppValue( 'activity', 'notify_setting_self', (int) $notify_setting_self ); $this->config->setAppValue( 'activity', 'notify_setting_selfemail', (int) $notify_setting_selfemail ); return new DataResponse(array( 'data' => array( 'message' => (string) $this->l10n->t('Settings have been updated.'), ), )); } /** * @NoAdminRequired * @NoCSRFRequired * * @return TemplateResponse */ public function displayPanel() { $settings = $this->manager->getSettings(); usort($settings, function(ISetting $a, ISetting $b) { if ($a->getPriority() === $b->getPriority()) { return $a->getIdentifier() > $b->getIdentifier(); } return $a->getPriority() > $b->getPriority(); }); $activities = []; foreach ($settings as $setting) { if (!$setting->canChangeStream() && !$setting->canChangeMail()) { // No setting can be changed => don't display continue; } $methods = []; if ($setting->canChangeStream()) { $methods[] = IExtension::METHOD_STREAM; } if ($setting->canChangeMail()) { $methods[] = IExtension::METHOD_MAIL; } $activities[$setting->getIdentifier()] = array( 'desc' => $setting->getName(), IExtension::METHOD_MAIL => $this->userSettings->getUserSetting($this->user, 'email', $setting->getIdentifier()), IExtension::METHOD_STREAM => $this->userSettings->getUserSetting($this->user, 'stream', $setting->getIdentifier()), 'methods' => $methods, ); } $settingBatchTime = UserSettings::EMAIL_SEND_HOURLY; $currentSetting = (int) $this->userSettings->getUserSetting($this->user, 'setting', 'batchtime'); if ($currentSetting === 3600 * 24 * 7) { $settingBatchTime = UserSettings::EMAIL_SEND_WEEKLY; } else if ($currentSetting === 3600 * 24) { $settingBatchTime = UserSettings::EMAIL_SEND_DAILY; } return new TemplateResponse('activity', 'settings/personal', [ 'setting' => 'personal', 'activities' => $activities, 'activity_email' => $this->config->getUserValue($this->user, 'settings', 'email', ''), 'setting_batchtime' => $settingBatchTime, 'notify_self' => $this->userSettings->getUserSetting($this->user, 'setting', 'self'), 'notify_selfemail' => $this->userSettings->getUserSetting($this->user, 'setting', 'selfemail'), 'methods' => [ IExtension::METHOD_MAIL => $this->l10n->t('Mail'), IExtension::METHOD_STREAM => $this->l10n->t('Stream'), ], ], ''); } /** * @NoAdminRequired * * @param string $enable 'true' if the feed is enabled * @return DataResponse */ public function feed($enable) { $token = $tokenUrl = ''; if ($enable === 'true') { $conflicts = true; // Check for collisions while (!empty($conflicts)) { $token = $this->random->generate(30, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); $conflicts = $this->config->getUsersForUserValue('activity', 'rsstoken', $token); } $tokenUrl = $this->urlGenerator->linkToRouteAbsolute('activity.Feed.show', ['token' => $token]); } $this->config->setUserValue($this->user, 'activity', 'rsstoken', $token); return new DataResponse(array( 'data' => array( 'message' => (string) $this->l10n->t('Your settings have been updated.'), 'rsslink' => $tokenUrl, ), )); } } Controller/APIv1.php 0000604 00000006434 15247130324 0010264 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity\Controller; use OCA\Activity\CurrentUser; use OCA\Activity\Data; use OCA\Activity\GroupHelper; use OCA\Activity\PlainTextParser; use OCA\Activity\UserSettings; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; class APIv1 extends OCSController { /** @var Data */ protected $data; /** @var GroupHelper */ protected $groupHelper; /** @var UserSettings */ protected $userSettings; /** @var PlainTextParser */ protected $parser; /** @var CurrentUser */ protected $currentUser; /** * @param string $appName * @param IRequest $request * @param Data $data * @param GroupHelper $groupHelper * @param UserSettings $userSettings * @param PlainTextParser $parser * @param CurrentUser $currentUser */ public function __construct($appName, IRequest $request, Data $data, GroupHelper $groupHelper, UserSettings $userSettings, PlainTextParser $parser, CurrentUser $currentUser) { parent::__construct($appName, $request); $this->data = $data; $this->userSettings = $userSettings; $this->groupHelper = $groupHelper; $this->parser = $parser; $this->currentUser = $currentUser; } /** * @NoAdminRequired * * @param int $start * @param int $count * @return DataResponse */ public function get($start = 0, $count = 30) { if ($start !== 0) { $start = $this->getSinceFromOffset($start); } $activities = $this->data->get( $this->groupHelper, $this->userSettings, $this->currentUser->getUID(), $start, $count, 'desc', 'all' ); $entries = array(); foreach($activities['data'] as $entry) { $entries[] = array( 'id' => $entry['activity_id'], 'subject' => $entry['subject'], 'message' => $entry['message'], 'file' => $entry['object_name'], 'link' => $entry['link'], 'date' => date('c', $entry['timestamp']), ); } return new DataResponse($entries); } /** * @param int $offset * @return int */ protected function getSinceFromOffset($offset) { $query = \OC::$server->getDatabaseConnection()->getQueryBuilder(); $query->select('activity_id') ->from('activity') ->where($query->expr()->eq('affecteduser', $query->createNamedParameter($this->currentUser->getUID()))) ->orderBy('activity_id', 'desc') ->setFirstResult($offset - 1) ->setMaxResults(1); $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); if ($row) { return (int) $row['activity_id']; } return 0; } } Controller/RemoteActivity.php 0000604 00000015315 15247130324 0012352 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity\Controller; use OCA\Activity\Extension\Files; use OCP\App\IAppManager; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\Files\InvalidPathException; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\IDBConnection; use OCP\IRequest; use OCP\Activity\IManager as IActivityManager; use OCP\IUser; use OCP\IUserManager; class RemoteActivity extends OCSController { /** @var IDBConnection */ protected $db; /** @var IUserManager */ protected $userManager; /** @var IAppManager */ protected $appManager; /** @var IRootFolder */ protected $rootFolder; /** @var IActivityManager */ protected $activityManager; public function __construct($appName, IRequest $request, IDBConnection $db, IUserManager $userManager, IAppManager $appManager, IRootFolder $rootFolder, IActivityManager $activityManager) { parent::__construct($appName, $request); $this->db = $db; $this->userManager = $userManager; $this->appManager = $appManager; $this->rootFolder = $rootFolder; $this->activityManager = $activityManager; } /** * @PublicPage * @NoCSRFRequired * * @param string $token * @param string[] $to * @param string[] $actor * @param string $type * @param string $updated * @param string[] $object * @param string[] $target * @param string[] $origin * @return DataResponse */ public function receiveActivity($token, array $to, array $actor, $type, $updated, array $object = [], array $target = [], array $origin = []) { $date = \DateTime::createFromFormat(\DateTime::W3C, $updated); if ($date === false) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } $time = $date->getTimestamp(); \OC::$server->getLogger()->warning(json_encode(func_get_args())); if (!isset($to['type'], $to['name']) || $to['type'] !== 'Person') { return new DataResponse([], Http::STATUS_BAD_REQUEST); } $user = $this->userManager->get($to['name']); if (!$user instanceof IUser) { return new DataResponse([], Http::STATUS_NOT_FOUND); } if (!isset($actor['type'], $actor['name']) || $actor['type'] !== 'Person') { return new DataResponse([], Http::STATUS_BAD_REQUEST); } if ($user->getCloudId() === $actor['name']) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } if (!$this->appManager->isInstalled('federatedfilesharing')) { return new DataResponse([], Http::STATUS_NOT_FOUND); } $query = $this->db->getQueryBuilder(); $query->select('*') ->from('share_external') ->where($query->expr()->eq('share_token', $query->createNamedParameter($token))) ->andWhere($query->expr()->eq('user', $query->createNamedParameter($user->getUID()))); $result = $query->execute(); $share = $result->fetch(); $result->closeCursor(); if (!is_array($share) || strpos($share['mountpoint'], '{{TemporaryMountPointName#') === 0) { return new DataResponse([], Http::STATUS_NOT_FOUND); } $internalType = $this->translateType($type); if ($internalType === '') { return new DataResponse([], Http::STATUS_BAD_REQUEST); } $path2 = null; if ($type === 'Move') { if (!isset($target['type'], $target['name']) || $target['type'] !== 'Document') { return new DataResponse([], Http::STATUS_BAD_REQUEST); } if (!isset($origin['type'], $origin['name']) || $origin['type'] !== 'Document') { return new DataResponse([], Http::STATUS_BAD_REQUEST); } $path = $share['mountpoint'] . $target['name']; $path2 = $share['mountpoint'] . $origin['name']; } else { if (!isset($object['type'], $object['name']) || $object['type'] !== 'Document') { return new DataResponse([], Http::STATUS_BAD_REQUEST); } $path = $share['mountpoint'] . $object['name']; } $subject = $this->getSubject($type, $path, $path2); if ($subject === '') { return new DataResponse([], Http::STATUS_BAD_REQUEST); } $userFolder = $this->rootFolder->getUserFolder($user->getUID()); try { $node = $userFolder->get($path); $fileId = $node->getId(); } catch (NotFoundException $e) { return new DataResponse([], Http::STATUS_NOT_FOUND); } catch (InvalidPathException $e) { return new DataResponse([], Http::STATUS_NOT_FOUND); } if ($path2 !== null) { $secondPath = [$fileId => $path2]; if ($subject === 'moved_by') { try { $parent = $node->getParent(); $secondPath = [$parent->getId() => dirname($path2)]; } catch (NotFoundException $e) { } catch (InvalidPathException $e) { } } $subjectParams = [$secondPath, $actor['name'], [$fileId => $path]]; } else { $subjectParams = [[$fileId => $path], $actor['name']]; } $event = $this->activityManager->generateEvent(); try { $event->setAffectedUser($user->getUID()) ->setApp('files') ->setType($internalType) ->setAuthor($actor['name']) ->setObject('files', $fileId, $path) ->setSubject($subject, $subjectParams) ->setTimestamp($time); $this->activityManager->publish($event); } catch (\InvalidArgumentException $e) { return new DataResponse(['activity'], Http::STATUS_BAD_REQUEST); } catch (\BadMethodCallException $e) { return new DataResponse(['sending'], Http::STATUS_BAD_REQUEST); } return new DataResponse(); } protected function getSubject($type, $path, $path2) { switch ($type) { case 'Create': return 'created_by'; case 'Move': if ($path2 === null) { return ''; } if (basename($path) === basename($path2)) { return 'moved_by'; } return 'renamed_by'; case 'Update': return 'changed_by'; case 'Delete': return 'deleted_by'; } return ''; } /** * @param string $type * @return string */ protected function translateType($type) { switch ($type) { case 'Create': return Files::TYPE_SHARE_CREATED; case 'Move': case 'Update': return Files::TYPE_SHARE_CHANGED; case 'Delete': return Files::TYPE_SHARE_DELETED; } return ''; } } Controller/Feed.php 0000604 00000007745 15247130324 0010255 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Controller; use OCA\Activity\Data; use OCA\Activity\GroupHelper; use OCA\Activity\PlainTextParser; use OCA\Activity\UserSettings; use OCP\Activity\IManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; use OCP\IURLGenerator; use OCP\L10N\IFactory; class Feed extends Controller { const DEFAULT_PAGE_SIZE = 30; /** @var \OCA\Activity\Data */ protected $data; /** @var \OCA\Activity\GroupHelper */ protected $helper; /** @var \OCA\Activity\UserSettings */ protected $settings; /** @var IURLGenerator */ protected $urlGenerator; /** @var IManager */ protected $activityManager; /** @var IConfig */ protected $config; /** @var IFactory */ protected $l10nFactory; /** @var IL10N */ protected $l; /** * constructor of the controller * * @param string $appName * @param IRequest $request * @param Data $data * @param GroupHelper $helper * @param UserSettings $settings * @param IURLGenerator $urlGenerator * @param IManager $activityManager * @param IFactory $l10nFactory * @param IConfig $config */ public function __construct($appName, IRequest $request, Data $data, GroupHelper $helper, UserSettings $settings, IURLGenerator $urlGenerator, IManager $activityManager, IFactory $l10nFactory, IConfig $config) { parent::__construct($appName, $request); $this->data = $data; $this->helper = $helper; $this->settings = $settings; $this->urlGenerator = $urlGenerator; $this->activityManager = $activityManager; $this->l10nFactory = $l10nFactory; $this->config = $config; } /** * @PublicPage * @NoCSRFRequired * * @return TemplateResponse */ public function show() { try { $user = $this->activityManager->getCurrentUserId(); $userLang = $this->config->getUserValue($user, 'core', 'lang'); // Overwrite user and language in the helper $this->l = $this->l10nFactory->get('activity', $userLang); $parser = new PlainTextParser($this->l); $this->helper->setL10n($this->l); $this->helper->setUser($user); $description = (string) $this->l->t('Personal activity feed for %s', $user); $response = $this->data->get($this->helper, $this->settings, $user, 0, self::DEFAULT_PAGE_SIZE, 'desc', 'all'); $activities = $response['data']; } catch (\UnexpectedValueException $e) { $this->l = $this->l10nFactory->get('activity'); $description = (string) $this->l->t('Your feed URL is invalid'); $activities = [ [ 'activity_id' => -1, 'timestamp' => time(), 'subject' => true, 'subject_prepared' => $description, ] ]; } $response = new TemplateResponse('activity', 'rss', [ 'rssLang' => $this->l->getLanguageCode(), 'rssLink' => $this->urlGenerator->linkToRouteAbsolute('activity.Feed.show'), 'rssPubDate' => date('r'), 'description' => $description, 'activities' => $activities, ], ''); if ($this->request->getHeader('accept') !== null && stristr($this->request->getHeader('accept'), 'application/rss+xml')) { $response->addHeader('Content-Type', 'application/rss+xml'); } else { $response->addHeader('Content-Type', 'text/xml; charset=UTF-8'); } return $response; } } Controller/APIv2.php 0000604 00000027260 15247130324 0010265 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Controller; use OC\Files\View; use OCA\Activity\Data; use OCA\Activity\Exception\InvalidFilterException; use OCA\Activity\GroupHelper; use OCA\Activity\UserSettings; use OCA\Activity\ViewInfoCache; use OCP\Activity\IManager; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\Files\FileInfo; use OCP\Files\IMimeTypeDetector; use OCP\IPreview; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserSession; class APIv2 extends OCSController { /** @var string */ protected $filter; /** @var int */ protected $since; /** @var int */ protected $limit; /** @var string */ protected $sort; /** @var string */ protected $objectType; /** @var int */ protected $objectId; /** @var string */ protected $user; /** @var bool */ protected $loadPreviews; /** @var IManager */ protected $activityManager; /** @var Data */ protected $data; /** @var GroupHelper */ protected $helper; /** @var UserSettings */ protected $settings; /** @var IURLGenerator */ protected $urlGenerator; /** @var IUserSession */ protected $userSession; /** @var IPreview */ protected $preview; /** @var IMimeTypeDetector */ protected $mimeTypeDetector; /** @var View */ protected $view; /** @var ViewInfoCache */ protected $infoCache; /** * OCSEndPoint constructor. * * @param string $appName * @param IRequest $request * @param IManager $activityManager * @param Data $data * @param GroupHelper $helper * @param UserSettings $settings * @param IURLGenerator $urlGenerator * @param IUserSession $userSession * @param IPreview $preview * @param IMimeTypeDetector $mimeTypeDetector * @param View $view * @param ViewInfoCache $infoCache */ public function __construct($appName, IRequest $request, IManager $activityManager, Data $data, GroupHelper $helper, UserSettings $settings, IURLGenerator $urlGenerator, IUserSession $userSession, IPreview $preview, IMimeTypeDetector $mimeTypeDetector, View $view, ViewInfoCache $infoCache) { parent::__construct($appName, $request); $this->activityManager = $activityManager; $this->data = $data; $this->helper = $helper; $this->settings = $settings; $this->urlGenerator = $urlGenerator; $this->userSession = $userSession; $this->preview = $preview; $this->mimeTypeDetector = $mimeTypeDetector; $this->view = $view; $this->infoCache = $infoCache; } /** * @param string $filter * @param int $since * @param int $limit * @param bool $previews * @param string $objectType * @param int $objectId * @param string $sort * @throws InvalidFilterException when the filter is invalid * @throws \OutOfBoundsException when no user is given */ protected function validateParameters($filter, $since, $limit, $previews, $objectType, $objectId, $sort) { $this->filter = is_string($filter) ? $filter : 'all'; if ($this->filter !== $this->data->validateFilter($this->filter)) { throw new InvalidFilterException(); } $this->since = (int) $since; $this->limit = (int) $limit; $this->loadPreviews = (bool) $previews; $this->objectType = (string) $objectType; $this->objectId = (int) $objectId; $this->sort = in_array($sort, ['asc', 'desc'], true) ? $sort : 'desc'; if (($this->objectType !== '' && $this->objectId === 0) || ($this->objectType === '' && $this->objectId !== 0)) { // Only allowed together $this->objectType = ''; $this->objectId = 0; } $user = $this->userSession->getUser(); if ($user instanceof IUser) { $this->user = $user->getUID(); } else { // No user logged in throw new \OutOfBoundsException(); } } /** * @NoAdminRequired * * @param int $since * @param int $limit * @param bool $previews * @param string $object_type * @param int $object_id * @param string $sort * @return DataResponse */ public function getDefault($since = 0, $limit = 50, $previews = false, $object_type = '', $object_id = 0, $sort = 'desc') { return $this->get('all', $since, $limit, $previews, $object_type, $object_id, $sort); } /** * @NoAdminRequired * * @param string $filter * @param int $since * @param int $limit * @param bool $previews * @param string $object_type * @param int $object_id * @param string $sort * @return DataResponse */ public function getFilter($filter, $since = 0, $limit = 50, $previews = false, $object_type = '', $object_id = 0, $sort = 'desc') { return $this->get($filter, $since, $limit, $previews, $object_type, $object_id, $sort); } /** * @param string $filter * @param int $since * @param int $limit * @param bool $previews * @param string $filterObjectType * @param int $filterObjectId * @param string $sort * @return DataResponse */ protected function get($filter, $since, $limit, $previews, $filterObjectType, $filterObjectId, $sort) { try { $this->validateParameters($filter, $since, $limit, $previews, $filterObjectType, $filterObjectId, $sort); } catch (InvalidFilterException $e) { return new DataResponse(null, Http::STATUS_NOT_FOUND); } catch (\OutOfBoundsException $e) { return new DataResponse(null, Http::STATUS_FORBIDDEN); } $this->activityManager->setRequirePNG($this->request->isUserAgent([IRequest::USER_AGENT_CLIENT_IOS])); try { $response = $this->data->get( $this->helper, $this->settings, $this->user, $this->since, $this->limit, $this->sort, $this->filter, $this->objectType, $this->objectId ); } catch (\OutOfBoundsException $e) { // Invalid since argument return new DataResponse(null, Http::STATUS_FORBIDDEN); } catch (\BadMethodCallException $e) { // No activity settings enabled return new DataResponse(null, Http::STATUS_NO_CONTENT); } $this->activityManager->setRequirePNG(false); $headers = $this->generateHeaders($response['headers'], $response['has_more'], $response['data']); if (empty($response['data']) || $this->request->getHeader('If-None-Match') === $headers['ETag']) { return new DataResponse([], Http::STATUS_NOT_MODIFIED, $headers); } $preparedActivities = []; foreach ($response['data'] as $activity) { $activity['datetime'] = date(\DateTime::ATOM, $activity['timestamp']); unset($activity['timestamp']); if ($this->loadPreviews) { $activity['previews'] = []; if ($activity['object_type'] === 'files') { if (!empty($activity['objects']) && is_array($activity['objects'])) { foreach ($activity['objects'] as $objectId => $objectName) { if (((int) $objectId) === 0 || $objectName === '') { // No file, no preview continue; } $activity['previews'][] = $this->getPreview($activity['affecteduser'], (int) $objectId, $objectName); } } else if ($activity['object_id']) { $activity['previews'][] = $this->getPreview($activity['affecteduser'], (int) $activity['object_id'], $activity['object_name']); } } } unset($activity['affecteduser']); $preparedActivities[] = $activity; } return new DataResponse($preparedActivities, Http::STATUS_OK, $headers); } /** * @param array $headers * @param bool $hasMoreActivities * @param array $data * @return array */ protected function generateHeaders(array $headers, $hasMoreActivities, array $data) { if ($hasMoreActivities && isset($headers['X-Activity-Last-Given'])) { // Set the "Link" header for the next page $nextPageParameters = [ 'since' => $headers['X-Activity-Last-Given'], 'limit' => $this->limit, 'sort' => $this->sort, ]; if ($this->objectType && $this->objectId) { $nextPageParameters['object_type'] = $this->objectType; $nextPageParameters['object_id'] = $this->objectId; } if ($this->request->getParam('format') !== null) { $nextPageParameters['format'] = $this->request->getParam('format'); } $nextPage = $this->request->getServerProtocol(); # http $nextPage .= '://' . $this->request->getServerHost(); # localhost $nextPage .= $this->request->getScriptName(); # /ocs/v2.php $nextPage .= $this->request->getPathInfo(); # /apps/activity/api/v2/activity $nextPage .= '?' . http_build_query($nextPageParameters); $headers['Link'] = '<' . $nextPage . '>; rel="next"'; } $ids = []; foreach ($data as $activity) { $ids[] = $activity['activity_id']; } $headers['ETag'] = md5(json_encode($ids)); return $headers; } /** * @param string $owner * @param int $fileId * @param string $filePath * @return array */ protected function getPreview($owner, $fileId, $filePath) { $info = $this->infoCache->getInfoById($owner, $fileId, $filePath); if (!$info['exists'] || $info['view'] !== '') { return $this->getPreviewFromPath($filePath, $info); } $preview = [ 'link' => $this->getPreviewLink($info['path'], $info['is_dir'], $info['view']), 'source' => '', 'isMimeTypeIcon' => true, ]; // show a preview image if the file still exists if ($info['is_dir']) { $preview['source'] = $this->getPreviewPathFromMimeType('dir'); } else { $this->view->chroot('/' . $owner . '/files'); $fileInfo = $this->view->getFileInfo($info['path']); if (!($fileInfo instanceof FileInfo)) { $pathPreview = $this->getPreviewFromPath($filePath, $info); $preview['source'] = $pathPreview['source']; } else if ($this->preview->isAvailable($fileInfo)) { $preview['isMimeTypeIcon'] = false; $preview['source'] = $this->urlGenerator->linkToRouteAbsolute('core.Preview.getPreview', [ 'file' => $info['path'], 'c' => $this->view->getETag($info['path']), 'x' => 150, 'y' => 150, ]); } else { $preview['source'] = $this->getPreviewPathFromMimeType($fileInfo->getMimetype()); } } return $preview; } /** * @param string $filePath * @param array $info * @return array */ protected function getPreviewFromPath($filePath, $info) { $mimeType = $info['is_dir'] ? 'dir' : $this->mimeTypeDetector->detectPath($filePath); $preview = [ 'link' => $this->getPreviewLink($info['path'], $info['is_dir'], $info['view']), 'source' => $this->getPreviewPathFromMimeType($mimeType), 'isMimeTypeIcon' => true, ]; return $preview; } /** * @param string $mimeType * @return string */ protected function getPreviewPathFromMimeType($mimeType) { $mimeTypeIcon = $this->mimeTypeDetector->mimeTypeIcon($mimeType); if (substr($mimeTypeIcon, -4) === '.png') { $mimeTypeIcon = substr($mimeTypeIcon, 0, -4) . '.svg'; } return $this->urlGenerator->getAbsoluteURL($mimeTypeIcon); } /** * @param string $path * @param bool $isDir * @param string $view * @return string */ protected function getPreviewLink($path, $isDir, $view) { $params = [ 'dir' => $path, ]; if (!$isDir) { $params['dir'] = (substr_count($path, '/') === 1) ? '/' : dirname($path); $params['scrollto'] = basename($path); } if ($view !== '') { $params['view'] = $view; } return $this->urlGenerator->linkToRouteAbsolute('files.view.index', $params); } } Controller/Activities.php 0000604 00000004630 15247130324 0011504 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Controller; use OCA\Activity\Data; use OCA\Activity\Navigation; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\IRequest; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; class Activities extends Controller { /** @var IConfig */ protected $config; /** @var Data */ protected $data; /** @var Navigation */ protected $navigation; /** @var EventDispatcherInterface */ protected $eventDispatcher; /** * @param string $appName * @param IRequest $request * @param IConfig $config * @param Data $data * @param Navigation $navigation * @param EventDispatcherInterface $eventDispatcher */ public function __construct($appName, IRequest $request, IConfig $config, Data $data, Navigation $navigation, EventDispatcherInterface $eventDispatcher) { parent::__construct($appName, $request); $this->data = $data; $this->config = $config; $this->navigation = $navigation; $this->eventDispatcher = $eventDispatcher; } /** * @NoAdminRequired * @NoCSRFRequired * * @param string $filter * @return TemplateResponse */ public function showList($filter = 'all') { $filter = $this->data->validateFilter($filter); $event = new GenericEvent($filter); $this->eventDispatcher->dispatch('OCA\Activity::loadAdditionalScripts', $event); return new TemplateResponse('activity', 'stream.body', [ 'appNavigation' => $this->navigation->getTemplate($filter), 'avatars' => $this->config->getSystemValue('enable_avatars', true) ? 'yes' : 'no', 'filter' => $filter, ]); } } CurrentUser.php 0000604 00000006415 15247130324 0007541 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity; use OCP\IRequest; use OCP\IUser; use OCP\IUserSession; use OCP\Share; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; class CurrentUser { /** @var IUserSession */ protected $userSession; /** @var IRequest */ protected $request; /** @var IManager */ protected $shareManager; /** @var string */ protected $identifier; /** @var string|null */ protected $cloudId; /** @var string|false|null */ protected $sessionUser; /** * @param IUserSession $userSession * @param IRequest $request * @param IManager $shareManager */ public function __construct(IUserSession $userSession, IRequest $request, IManager $shareManager) { $this->userSession = $userSession; $this->request = $request; $this->shareManager = $shareManager; $this->cloudId = false; $this->sessionUser = false; } /** * Get an identifier for the user, session or token * @return string */ public function getUserIdentifier() { if ($this->identifier === null) { $this->identifier = $this->getUID(); if ($this->identifier === null) { $this->identifier = $this->getCloudIDFromToken(); if ($this->identifier === null) { // Nothing worked, fallback to empty string $this->identifier = ''; } } } return $this->identifier; } /** * Get the current user from the session * @return string|null */ public function getUID() { if ($this->sessionUser === false) { $user = $this->userSession->getUser(); if ($user instanceof IUser) { $this->sessionUser = (string) $user->getUID(); } else { $this->sessionUser = null; } } return $this->sessionUser; } /** * Get the current user from the session * @return string|null */ public function getCloudId() { if ($this->cloudId === false) { $user = $this->userSession->getUser(); if ($user instanceof IUser) { $this->cloudId = (string) $user->getCloudId(); } else { $this->cloudId = $this->getCloudIDFromToken(); } } return $this->cloudId; } /** * Get the cloud ID from the sharing token * @return string|null */ protected function getCloudIDFromToken() { if (!empty($this->request->server['PHP_AUTH_USER'])) { $token = $this->request->server['PHP_AUTH_USER']; try { $share = $this->shareManager->getShareByToken($token); if ($share->getShareType() === Share::SHARE_TYPE_REMOTE) { return $share->getSharedWith(); } } catch (ShareNotFound $e) { // No share, use the fallback } } return null; } } PlainTextParser.php 0000604 00000007350 15247130324 0010344 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCP\IL10N; class PlainTextParser { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * Parse the parameters in the subject and message * * @param string $message * @return string */ public function parseMessage($message) { $message = $this->parseCollections($message); $message = $this->parseParameters($message); return $message; } /** * Parse collections * * @param string $message * @return string */ protected function parseCollections($message) { return preg_replace_callback('/<collection>(.*?)<\/collection>/', function($match) { $parameterList = explode('><', $match[1]); $parameterListLength = sizeof($parameterList); $parameters = []; for ($i = 0; $i < $parameterListLength; $i++) { $parameter = $parameterList[$i]; if ($i > 0) { $parameter = '<' . $parameter; } if ($i + 1 < $parameterListLength) { $parameter = $parameter . '>'; } $parameters[] = $this->parseParameters($parameter); } if ($parameterListLength === 1) { return array_pop($parameters); } else { $lastParameter = array_pop($parameters); return $this->l->t('%s and %s', [ implode($this->l->t(', '), $parameters), $lastParameter, ]); } }, $message); } /** * Parse the parameters in the subject and message * * @param string $message * @return string */ protected function parseParameters($message) { $message = $this->parseUntypedParameters($message); $message = $this->parseUserParameters($message); $message = $this->parseFederatedCloudIDParameters($message); $message = $this->parseFileParameters($message); return $message; } /** * Display the parameter value * * @param string $message * @return string */ protected function parseUntypedParameters($message) { return preg_replace_callback('/<parameter>(.*?)<\/parameter>/', function($match) { return $match[1]; }, $message); } /** * Display the users display name * * @param string $message * @return string */ protected function parseUserParameters($message) { return preg_replace_callback('/<user\ display\-name=\"(.*?)\">(.*?)<\/user>/', function($match) { // We don't want HTML to work, but quote signs are okay. return str_replace('"', '"', $match[1]); }, $message); } /** * Display the full cloud id * * @param string $message * @return string */ protected function parseFederatedCloudIDParameters($message) { return preg_replace_callback('/<federated-cloud-id\ display\-name=\"(.*?)\"\ user=\"(.*?)\"\ server=\"(.*?)\">(.*?)<\/federated-cloud-id>/', function($match) { return $match[1]; }, $message); } /** * Display the path for files * * @param string $message * @return string */ protected function parseFileParameters($message) { return preg_replace_callback('/<file\ link=\"(.*?)\"\ id=\"(.*?)\">(.*?)<\/file>/', function($match) { return $match[3]; }, $message); } } MailQueueHandler.php 0000604 00000033226 15247130324 0010445 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCA\Activity\Extension\LegacyParser; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\IConfig; use OCP\IDateTimeFormatter; use OCP\IDBConnection; use OCP\ILogger; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory; use OCP\Mail\IMailer; use OCP\Util; /** * Class MailQueueHandler * Gets the users from the database and * * @package OCA\Activity */ class MailQueueHandler { const CLI_EMAIL_BATCH_SIZE = 500; const WEB_EMAIL_BATCH_SIZE = 25; /** Number of entries we want to list in the email */ const ENTRY_LIMIT = 200; /** @var array */ protected $languages; /** @var string */ protected $senderAddress; /** @var string */ protected $senderName; /** @var IDateTimeFormatter */ protected $dateFormatter; /** @var DataHelper */ protected $dataHelper; /** @var IDBConnection */ protected $connection; /** @var IMailer */ protected $mailer; /** @var IURLGenerator */ protected $urlGenerator; /** @var IUserManager */ protected $userManager; /** @var IFactory */ protected $lFactory; /** @var IManager */ protected $activityManager; /** @var LegacyParser */ protected $legacyParser; /** @var IConfig */ protected $config; /** @var ILogger */ protected $logger; /** * Constructor * * @param IDateTimeFormatter $dateFormatter * @param IDBConnection $connection * @param DataHelper $dataHelper * @param IMailer $mailer * @param IURLGenerator $urlGenerator * @param IUserManager $userManager * @param IFactory $lFactory * @param IManager $activityManager * @param LegacyParser $legacyParser * @param IConfig $config * @param ILogger $logger */ public function __construct(IDateTimeFormatter $dateFormatter, IDBConnection $connection, DataHelper $dataHelper, IMailer $mailer, IURLGenerator $urlGenerator, IUserManager $userManager, IFactory $lFactory, IManager $activityManager, LegacyParser $legacyParser, IConfig $config, ILogger $logger) { $this->dateFormatter = $dateFormatter; $this->connection = $connection; $this->dataHelper = $dataHelper; $this->mailer = $mailer; $this->urlGenerator = $urlGenerator; $this->userManager = $userManager; $this->lFactory = $lFactory; $this->activityManager = $activityManager; $this->legacyParser = $legacyParser; $this->config = $config; $this->logger = $logger; } /** * Send an email to {$limit} users * * @param int $limit Number of users we want to send an email to * @param int $sendTime The latest send time * @param bool $forceSending Ignores latest send and just sends all emails * @param null|int $restrictEmails null or one of UserSettings::EMAIL_SEND_* * @return int Number of users we sent an email to */ public function sendEmails($limit, $sendTime, $forceSending = false, $restrictEmails = null) { // Get all users which should receive an email $affectedUsers = $this->getAffectedUsers($limit, $sendTime, $forceSending, $restrictEmails); if (empty($affectedUsers)) { // No users found to notify, mission abort return 0; } $userLanguages = $this->config->getUserValueForUsers('core', 'lang', $affectedUsers); $userTimezones = $this->config->getUserValueForUsers('core', 'timezone', $affectedUsers); $userEmails = $this->config->getUserValueForUsers('settings', 'email', $affectedUsers); // Send Email $default_lang = $this->config->getSystemValue('default_language', 'en'); $defaultTimeZone = date_default_timezone_get(); $deleteItemsForUsers = []; $this->activityManager->setRequirePNG(true); foreach ($affectedUsers as $user) { if (empty($userEmails[$user])) { // The user did not setup an email address // So we will not send an email :( $this->logger->debug("Couldn't send notification email to user '{user}' (email address isn't set for that user)", ['user' => $user, 'app' => 'activity']); continue; } $language = (!empty($userLanguages[$user])) ? $userLanguages[$user] : $default_lang; $timezone = (!empty($userTimezones[$user])) ? $userTimezones[$user] : $defaultTimeZone; try { if ($this->sendEmailToUser($user, $userEmails[$user], $language, $timezone, $sendTime)) { $deleteItemsForUsers[] = $user; } else { $this->logger->debug("Failed sending activity email to user '{user}'.", ['user' => $user, 'app' => 'activity']); } } catch (\Exception $e) { $this->logger->logException($e, [ 'message' => 'Failed sending activity email to user "{user}"', 'user' => $user, 'app' => 'activity', ]); // continue; } } $this->activityManager->setRequirePNG(false); // Delete all entries we dealt with $this->deleteSentItems($deleteItemsForUsers, $sendTime); return count($affectedUsers); } /** * Get the users we want to send an email to * * @param int|null $limit * @param int $latestSend * @param bool $forceSending * @param int|null $restrictEmails * @return array */ protected function getAffectedUsers($limit, $latestSend, $forceSending, $restrictEmails) { $query = $this->connection->getQueryBuilder(); $query->select('amq_affecteduser') ->selectAlias($query->createFunction('MIN(' . $query->getColumnName('amq_latest_send') . ')'), 'amq_trigger_time') ->from('activity_mq') ->groupBy('amq_affecteduser') ->orderBy('amq_trigger_time', 'ASC'); if ($limit > 0) { $query->setMaxResults($limit); } if ($forceSending) { $query->where($query->expr()->lt('amq_timestamp', $query->createNamedParameter($latestSend))); } else { $query->where($query->expr()->lt('amq_latest_send', $query->createNamedParameter($latestSend))); } if ($restrictEmails !== null) { if ($restrictEmails === UserSettings::EMAIL_SEND_HOURLY) { $query->where($query->expr()->lte('amq_timestamp', $query->createFunction($query->getColumnName('amq_latest_send') . ' + ' . 3600))); } else if ($restrictEmails === UserSettings::EMAIL_SEND_DAILY) { $query->where($query->expr()->eq('amq_timestamp', $query->createFunction($query->getColumnName('amq_latest_send') . ' + ' . 3600 * 24))); } else if ($restrictEmails === UserSettings::EMAIL_SEND_WEEKLY) { $query->where($query->expr()->eq('amq_timestamp', $query->createFunction($query->getColumnName('amq_latest_send') . ' + ' . 3600 * 24 * 7))); } } $result = $query->execute(); $affectedUsers = array(); while ($row = $result->fetch()) { $affectedUsers[] = $row['amq_affecteduser']; } $result->closeCursor(); return $affectedUsers; } /** * Get all items for the user we want to send an email to * * @param string $affectedUser * @param int $maxTime * @param int $maxNumItems * @return array [data of the first max. 200 entries, total number of entries] */ protected function getItemsForUser($affectedUser, $maxTime, $maxNumItems = self::ENTRY_LIMIT) { $query = $this->connection->prepare( 'SELECT * ' . ' FROM `*PREFIX*activity_mq` ' . ' WHERE `amq_timestamp` <= ? ' . ' AND `amq_affecteduser` = ? ' . ' ORDER BY `amq_timestamp` ASC', $maxNumItems ); $query->execute([(int) $maxTime, $affectedUser]); $activities = array(); while ($row = $query->fetch()) { $activities[] = $row; } if (isset($activities[$maxNumItems - 1])) { // Reached the limit, run a query to get the actual count. $query = $this->connection->prepare( 'SELECT COUNT(*) AS `actual_count`' . ' FROM `*PREFIX*activity_mq` ' . ' WHERE `amq_timestamp` <= ? ' . ' AND `amq_affecteduser` = ?' ); $query->execute([(int) $maxTime, $affectedUser]); $row = $query->fetch(); return [$activities, $row['actual_count'] - $maxNumItems]; } else { return [$activities, 0]; } } /** * Get a language object for a specific language * * @param string $lang Language identifier * @return \OCP\IL10N Language object of $lang */ protected function getLanguage($lang) { if (!isset($this->languages[$lang])) { $this->languages[$lang] = $this->lFactory->get('activity', $lang); } return $this->languages[$lang]; } /** * Get the sender data * @param string $setting Either `email` or `name` * @return string */ protected function getSenderData($setting) { if (empty($this->senderAddress)) { $this->senderAddress = Util::getDefaultEmailAddress('no-reply'); } if (empty($this->senderName)) { $defaults = new Defaults(); $this->senderName = $defaults->getName(); } if ($setting === 'email') { return $this->senderAddress; } return $this->senderName; } /** * Send a notification to one user * * @param string $userName Username of the recipient * @param string $email Email address of the recipient * @param string $lang Selected language of the recipient * @param string $timezone Selected timezone of the recipient * @param int $maxTime * @return bool True if the entries should be removed, false otherwise * @throws \UnexpectedValueException */ protected function sendEmailToUser($userName, $email, $lang, $timezone, $maxTime) { $user = $this->userManager->get($userName); if (!$user instanceof IUser) { return true; } list($mailData, $skippedCount) = $this->getItemsForUser($userName, $maxTime); $l = $this->getLanguage($lang); $this->dataHelper->setUser($userName); $this->dataHelper->setL10n($l); $this->activityManager->setCurrentUserId($userName); $activityEvents = []; foreach ($mailData as $activity) { $event = $this->activityManager->generateEvent(); try { $event->setApp($activity['amq_appid']) ->setType($activity['amq_type']) ->setTimestamp((int) $activity['amq_timestamp']) ->setSubject($activity['amq_subject'], json_decode($activity['amq_subjectparams'], true)); } catch (\InvalidArgumentException $e) { continue; } $relativeDateTime = $this->dateFormatter->formatDateTimeRelativeDay( $activity['amq_timestamp'], 'long', 'short', new \DateTimeZone($timezone), $l ); try { $event = $this->parseEvent($lang, $event); } catch (\InvalidArgumentException $e) { continue; } $activityEvents[] = [ 'event' => $event, 'relativeDateTime' => $relativeDateTime ]; } $template = $this->mailer->createEMailTemplate('activity.Notification', [ 'displayname' => $user->getDisplayName(), 'url' => $this->urlGenerator->getAbsoluteURL('/'), 'activityEvents' => $activityEvents, 'skippedCount' => $skippedCount, ]); $template->addHeader(); $template->addHeading($l->t('Hello %s',[$user->getDisplayName()]), $l->t('Hello %s,',[$user->getDisplayName()])); $template->addBodyText($l->t('There was some activity at %s', [$this->urlGenerator->getAbsoluteURL('/')])); foreach ($activityEvents as $activity) { /** @var IEvent $event */ $event = $activity['event']; $relativeDateTime = $activity['relativeDateTime']; $template->addBodyListItem($event->getParsedSubject(), $relativeDateTime, $event->getIcon()); } if ($skippedCount) { $template->addBodyListItem($l->n('and %n more ', 'and %n more ', $skippedCount)); } $template->addFooter(); $message = $this->mailer->createMessage(); $message->setTo([$email => $user->getDisplayName()]); $message->setSubject((string) $l->t('Activity notification')); $message->setHtmlBody($template->renderHtml()); $message->setPlainBody($template->renderText()); $message->setFrom([$this->getSenderData('email') => $this->getSenderData('name')]); try { $this->mailer->send($message); } catch (\Exception $e) { return false; } $this->activityManager->setCurrentUserId(null); return true; } /** * @param string $lang * @param IEvent $event * @return IEvent * @throws \InvalidArgumentException when the event could not be parsed */ protected function parseEvent($lang, IEvent $event) { foreach ($this->activityManager->getProviders() as $provider) { try { $this->activityManager->setFormattingObject($event->getObjectType(), $event->getObjectId()); $event = $provider->parse($lang, $event); $this->activityManager->setFormattingObject('', 0); } catch (\InvalidArgumentException $e) { } } if (!$event->getParsedSubject()) { $this->activityManager->setFormattingObject($event->getObjectType(), $event->getObjectId()); $event = $this->legacyParser->parse($lang, $event); $this->activityManager->setFormattingObject('', 0); } return $event; } /** * Delete all entries we dealt with * * @param array $affectedUsers * @param int $maxTime */ protected function deleteSentItems(array $affectedUsers, $maxTime) { if (empty($affectedUsers)) { return; } $query = $this->connection->getQueryBuilder(); $query->delete('activity_mq') ->where($query->expr()->lte('amq_timestamp', $query->createNamedParameter($maxTime, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->in('amq_affecteduser', $query->createNamedParameter($affectedUsers, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR)); $query->execute(); } } FilesHooks.php 0000604 00000104713 15247130324 0007326 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Frank Karlitschek <frank@karlitschek.de> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OC\Files\Filesystem; use OC\Files\View; use OCA\Activity\BackgroundJob\RemoteActivity; use OCA\Activity\Extension\Files; use OCA\Activity\Extension\Files_Sharing; use OCP\Activity\IManager; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\IDBConnection; use OCP\IGroup; use OCP\IGroupManager; use OCP\ILogger; use OCP\IURLGenerator; use OCP\IUser; use OCP\Share; use OCP\Share\IShare; use OCP\Share\IShareHelper; /** * The class to handle the filesystem hooks */ class FilesHooks { const USER_BATCH_SIZE = 50; /** @var \OCP\Activity\IManager */ protected $manager; /** @var \OCA\Activity\Data */ protected $activityData; /** @var \OCA\Activity\UserSettings */ protected $userSettings; /** @var \OCP\IGroupManager */ protected $groupManager; /** @var \OCP\IDBConnection */ protected $connection; /** @var \OC\Files\View */ protected $view; /** @var IRootFolder */ protected $rootFolder; /** @var IShareHelper */ protected $shareHelper; /** @var IURLGenerator */ protected $urlGenerator; /** @var ILogger */ protected $logger; /** @var CurrentUser */ protected $currentUser; /** @var string|bool */ protected $moveCase = false; /** @var array */ protected $oldAccessList; /** @var string */ protected $oldParentPath; /** @var string */ protected $oldParentOwner; /** @var string */ protected $oldParentId; /** * Constructor * * @param IManager $manager * @param Data $activityData * @param UserSettings $userSettings * @param IGroupManager $groupManager * @param View $view * @param IRootFolder $rootFolder * @param IShareHelper $shareHelper * @param IDBConnection $connection * @param IURLGenerator $urlGenerator * @param ILogger $logger * @param CurrentUser $currentUser */ public function __construct(IManager $manager, Data $activityData, UserSettings $userSettings, IGroupManager $groupManager, View $view, IRootFolder $rootFolder, IShareHelper $shareHelper, IDBConnection $connection, IURLGenerator $urlGenerator, ILogger $logger, CurrentUser $currentUser) { $this->manager = $manager; $this->activityData = $activityData; $this->userSettings = $userSettings; $this->groupManager = $groupManager; $this->view = $view; $this->rootFolder = $rootFolder; $this->shareHelper = $shareHelper; $this->connection = $connection; $this->urlGenerator = $urlGenerator; $this->logger = $logger; $this->currentUser = $currentUser; } /** * Store the create hook events * @param string $path Path of the file that has been created */ public function fileCreate($path) { if ($path === '/' || $path === '' || $path === null) { return; } if ($this->currentUser->getUserIdentifier() !== '') { $this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CREATED, 'created_self', 'created_by'); } else { $this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CREATED, '', 'created_public'); } } /** * Store the update hook events * @param string $path Path of the file that has been modified */ public function fileUpdate($path) { $this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CHANGED, 'changed_self', 'changed_by'); } /** * Store the delete hook events * @param string $path Path of the file that has been deleted */ public function fileDelete($path) { $this->addNotificationsForFileAction($path, Files::TYPE_SHARE_DELETED, 'deleted_self', 'deleted_by'); } /** * Store the restore hook events * @param string $path Path of the file that has been restored */ public function fileRestore($path) { $this->addNotificationsForFileAction($path, Files::TYPE_SHARE_RESTORED, 'restored_self', 'restored_by'); } /** * Creates the entries for file actions on $file_path * * @param string $filePath The file that is being changed * @param int $activityType The activity type * @param string $subject The subject for the actor * @param string $subjectBy The subject for other users (with "by $actor") */ protected function addNotificationsForFileAction($filePath, $activityType, $subject, $subjectBy) { // Do not add activities for .part-files if (substr($filePath, -5) === '.part') { return; } list($filePath, $uidOwner, $fileId) = $this->getSourcePathAndOwner($filePath); if ($fileId === 0) { // Could not find the file for the owner ... return; } $accessList = $this->getUserPathsFromPath($filePath, $uidOwner); $this->generateRemoteActivity($accessList['remotes'], $activityType, time(), $this->currentUser->getCloudId(), $accessList['ownerPath']); $affectedUsers = $accessList['users']; $filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', $activityType); $filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', $activityType); foreach ($affectedUsers as $user => $path) { $user = (string) $user; if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { continue; } if ($user === $this->currentUser->getUID()) { $userSubject = $subject; $userParams = [[$fileId => $path]]; } else { $userSubject = $subjectBy; $userParams = [[$fileId => $path], $this->currentUser->getUserIdentifier()]; } $this->addNotificationsForUser( $user, $userSubject, $userParams, $fileId, $path, true, !empty($filteredStreamUsers[$user]), !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, $activityType ); } } protected function generateRemoteActivity(array $remoteUsers, $type, $time, $actor, $ownerPath = false) { foreach ($remoteUsers as $remoteUser => $info) { if ($actor === $remoteUser) { // Current user receives the notification on their own instance already continue; } $arguments = [ $remoteUser, $info['token'], $ownerPath !== false ? substr($ownerPath, strlen($info['node_path'])) : $info['node_path'], $type, $time, $actor, ]; if (isset($info['second_path'])) { $arguments[] = $info['second_path']; } \OC::$server->getJobList()->add(RemoteActivity::class, $arguments); } } /** * Collect some information for move/renames * * @param string $oldPath Path of the file that has been moved * @param string $newPath Path of the file that has been moved */ public function fileMove($oldPath, $newPath) { if (substr($oldPath, -5) === '.part' || substr($newPath, -5) === '.part') { // Do not add activities for .part-files $this->moveCase = false; return; } $oldDir = dirname($oldPath); $newDir = dirname($newPath); if ($oldDir === $newDir) { /** * a/b moved to a/c * * Cases: * - a/b shared: no visible change * - a/ shared: rename */ $this->moveCase = 'rename'; return; } if (strpos($oldDir, $newDir) === 0) { /** * a/b/c moved to a/c * * Cases: * - a/b/c shared: no visible change * - a/b/ shared: delete * - a/ shared: move/rename */ $this->moveCase = 'moveUp'; } else if (strpos($newDir, $oldDir) === 0) { /** * a/b moved to a/c/b * * Cases: * - a/b shared: no visible change * - a/c/ shared: add * - a/ shared: move/rename */ $this->moveCase = 'moveDown'; } else { /** * a/b/c moved to a/d/c * * Cases: * - a/b/c shared: no visible change * - a/b/ shared: delete * - a/d/ shared: add * - a/ shared: move/rename */ $this->moveCase = 'moveCross'; } list($this->oldParentPath, $this->oldParentOwner, $this->oldParentId) = $this->getSourcePathAndOwner($oldDir); if ($this->oldParentId === 0) { // Could not find the file for the owner ... $this->moveCase = false; return; } $this->oldAccessList = $this->getUserPathsFromPath($this->oldParentPath, $this->oldParentOwner); } /** * Store the move hook events * * @param string $oldPath Path of the file that has been moved * @param string $newPath Path of the file that has been moved */ public function fileMovePost($oldPath, $newPath) { // Do not add activities for .part-files if ($this->moveCase === false) { return; } switch ($this->moveCase) { case 'rename': $this->fileRenaming($oldPath, $newPath); break; case 'moveUp': case 'moveDown': case 'moveCross': $this->fileMoving($oldPath, $newPath); break; } $this->moveCase = false; } /** * Renaming a file inside the same folder (a/b to a/c) * * @param string $oldPath * @param string $newPath */ protected function fileRenaming($oldPath, $newPath) { $dirName = dirname($newPath); $fileName = basename($newPath); $oldFileName = basename($oldPath); list(, , $fileId) = $this->getSourcePathAndOwner($newPath); list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName); if ($fileId === 0 || $parentId === 0) { // Could not find the file for the owner ... return; } $accessList = $this->getUserPathsFromPath($parentPath, $parentOwner); $renameRemotes = []; foreach ($accessList['remotes'] as $remote => $info) { $renameRemotes[$remote] = [ 'token' => $info['token'], 'node_path' => substr($newPath, strlen($info['node_path'])), 'second_path' => substr($oldPath, strlen($info['node_path'])), ]; } $this->generateRemoteActivity($renameRemotes, Files::TYPE_SHARE_CHANGED, time(), $this->currentUser->getCloudId()); $affectedUsers = $accessList['users']; $filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', Files::TYPE_SHARE_CHANGED); $filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', Files::TYPE_SHARE_CHANGED); foreach ($affectedUsers as $user => $path) { if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { continue; } if ($user === $this->currentUser->getUID()) { $userSubject = 'renamed_self'; $userParams = [ [$fileId => $path . '/' . $fileName], [$fileId => $path . '/' . $oldFileName], ]; } else { $userSubject = 'renamed_by'; $userParams = [ [$fileId => $path . '/' . $fileName], $this->currentUser->getUserIdentifier(), [$fileId => $path . '/' . $oldFileName], ]; } $this->addNotificationsForUser( $user, $userSubject, $userParams, $fileId, $path . '/' . $fileName, true, !empty($filteredStreamUsers[$user]), !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, Files::TYPE_SHARE_CHANGED ); } } /** * Moving a file from one folder to another * * @param string $oldPath * @param string $newPath */ protected function fileMoving($oldPath, $newPath) { $dirName = dirname($newPath); $fileName = basename($newPath); $oldFileName = basename($oldPath); list(, , $fileId) = $this->getSourcePathAndOwner($newPath); list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName); if ($fileId === 0 || $parentId === 0) { // Could not find the file for the owner ... return; } $accessList = $this->getUserPathsFromPath($parentPath, $parentOwner); $affectedUsers = $accessList['users']; $oldUsers = $this->oldAccessList['users']; $beforeUsers = array_keys($oldUsers); $afterUsers = array_keys($affectedUsers); $deleteUsers = array_diff($beforeUsers, $afterUsers); $this->generateDeleteActivities($deleteUsers, $oldUsers, $fileId, $oldFileName); $addUsers = array_diff($afterUsers, $beforeUsers); $this->generateAddActivities($addUsers, $affectedUsers, $fileId, $fileName); $moveUsers = array_intersect($beforeUsers, $afterUsers); $this->generateMoveActivities($moveUsers, $oldUsers, $affectedUsers, $fileId, $oldFileName, $parentId, $fileName); $beforeRemotes = $this->oldAccessList['remotes']; $afterRemotes = $accessList['remotes']; $addRemotes = $deleteRemotes = $moveRemotes = []; foreach ($afterRemotes as $remote => $info) { if (isset($beforeRemotes[$remote])) { // Move $info['node_path'] = substr($newPath, strlen($info['node_path'])); $info['second_path'] = substr($oldPath, strlen($beforeRemotes[$remote]['node_path'])); $moveRemotes[$remote] = $info; } else { $info['node_path'] = substr($newPath, strlen($info['node_path'])); $addRemotes[$remote] = $info; } } foreach ($beforeRemotes as $remote => $info) { if (!isset($afterRemotes[$remote])) { $info['node_path'] = substr($oldPath, strlen($info['node_path'])); $deleteRemotes[$remote] = $info; } } $this->generateRemoteActivity($deleteRemotes, Files::TYPE_SHARE_DELETED, time(), $this->currentUser->getCloudId()); $this->generateRemoteActivity($addRemotes, Files::TYPE_SHARE_CREATED, time(), $this->currentUser->getCloudId()); $this->generateRemoteActivity($moveRemotes, Files::TYPE_SHARE_CHANGED, time(), $this->currentUser->getCloudId()); } /** * @param string[] $users * @param string[] $pathMap * @param int $fileId * @param string $oldFileName */ protected function generateDeleteActivities($users, $pathMap, $fileId, $oldFileName) { if (empty($users)) { return; } $filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_DELETED); $filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_DELETED); foreach ($users as $user) { if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { continue; } $path = $pathMap[$user]; if ($user === $this->currentUser->getUID()) { $userSubject = 'deleted_self'; $userParams = [[$fileId => $path . '/' . $oldFileName]]; } else { $userSubject = 'deleted_by'; $userParams = [[$fileId => $path . '/' . $oldFileName], $this->currentUser->getUserIdentifier()]; } $this->addNotificationsForUser( $user, $userSubject, $userParams, $fileId, $path . '/' . $oldFileName, true, !empty($filteredStreamUsers[$user]), !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, Files::TYPE_SHARE_DELETED ); } } /** * @param string[] $users * @param string[] $pathMap * @param int $fileId * @param string $fileName */ protected function generateAddActivities($users, $pathMap, $fileId, $fileName) { if (empty($users)) { return; } $filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_CREATED); $filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_CREATED); foreach ($users as $user) { if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { continue; } $path = $pathMap[$user]; if ($user === $this->currentUser->getUID()) { $userSubject = 'created_self'; $userParams = [[$fileId => $path . '/' . $fileName]]; } else { $userSubject = 'created_by'; $userParams = [[$fileId => $path . '/' . $fileName], $this->currentUser->getUserIdentifier()]; } $this->addNotificationsForUser( $user, $userSubject, $userParams, $fileId, $path . '/' . $fileName, true, !empty($filteredStreamUsers[$user]), !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, Files::TYPE_SHARE_CREATED ); } } /** * @param string[] $users * @param string[] $beforePathMap * @param string[] $afterPathMap * @param int $fileId * @param string $oldFileName * @param int $newParentId * @param string $fileName */ protected function generateMoveActivities($users, $beforePathMap, $afterPathMap, $fileId, $oldFileName, $newParentId, $fileName) { if (empty($users)) { return; } $filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_CHANGED); $filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_CHANGED); foreach ($users as $user) { if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { continue; } if ($oldFileName === $fileName) { $userParams = [[$newParentId => $afterPathMap[$user] . '/']]; } else { $userParams = [[$fileId => $afterPathMap[$user] . '/' . $fileName]]; } if ($user === $this->currentUser->getUID()) { $userSubject = 'moved_self'; } else { $userSubject = 'moved_by'; $userParams[] = $this->currentUser->getUserIdentifier(); } $userParams[] = [$fileId => $beforePathMap[$user] . '/' . $oldFileName]; $this->addNotificationsForUser( $user, $userSubject, $userParams, $fileId, $afterPathMap[$user] . '/' . $fileName, true, !empty($filteredStreamUsers[$user]), !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, Files::TYPE_SHARE_CHANGED ); } } /** * Returns a "username => path" map for all affected users * * @param string $path * @param string $uidOwner * @return array */ protected function getUserPathsFromPath($path, $uidOwner) { try { $node = $this->rootFolder->getUserFolder($uidOwner)->get($path); } catch (NotFoundException $e) { return []; } if (!$node instanceof Node) { return []; } $accessList = $this->shareHelper->getPathsForAccessList($node); $path = $node->getPath(); $sections = explode('/', $path, 4); $accessList['ownerPath'] = '/' . $sections[3]; return $accessList; } /** * Return the source * * @param string $path * @return array */ protected function getSourcePathAndOwner($path) { $view = Filesystem::getView(); $owner = $view->getOwner($path); $owner = !is_string($owner) || $owner === '' ? null : $owner; $fileId = 0; $currentUser = $this->currentUser->getUID(); if ($owner === null || $owner !== $currentUser) { /** @var \OCP\Files\Storage\IStorage $storage */ list($storage,) = $view->resolvePath($path); if ($owner !== null && !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { Filesystem::initMountPoints($owner); } else { // Probably a remote user, let's try to at least generate activities // for the current user if ($currentUser === null) { list(,$owner,) = explode('/', $view->getAbsolutePath($path), 3); } else { $owner = $currentUser; } } } $info = Filesystem::getFileInfo($path); if ($info !== false) { $ownerView = new View('/' . $owner . '/files'); $fileId = (int) $info['fileid']; $path = $ownerView->getPath($fileId); } return array($path, $owner, $fileId); } /** * Manage sharing events * @param array $params The hook params */ public function share($params) { if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) { $this->shareWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget']); } else if ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) { $this->shareWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id']); } else if ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) { $this->shareByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner']); } } } /** * Sharing a file or folder with a user * * @param string $shareWith * @param int $fileSource File ID that is being shared * @param string $itemType File type that is being shared (file or folder) * @param string $fileTarget File path */ protected function shareWithUser($shareWith, $fileSource, $itemType, $fileTarget) { // User performing the share $this->shareNotificationForSharer('shared_user_self', $shareWith, $fileSource, $itemType); if ($this->currentUser->getUID() !== null) { $this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_user_by', $shareWith, $fileSource, $itemType); } // New shared user $this->addNotificationsForUser( $shareWith, 'shared_with_by', [[$fileSource => $fileTarget], $this->currentUser->getUserIdentifier()], (int) $fileSource, $fileTarget, $itemType === 'file', $this->userSettings->getUserSetting($shareWith, 'stream', Files_Sharing::TYPE_SHARED), $this->userSettings->getUserSetting($shareWith, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($shareWith, 'setting', 'batchtime') : 0 ); } /** * Sharing a file or folder with a group * * @param string $shareWith * @param int $fileSource File ID that is being shared * @param string $itemType File type that is being shared (file or folder) * @param string $fileTarget File path * @param int $shareId The Share ID of this share */ protected function shareWithGroup($shareWith, $fileSource, $itemType, $fileTarget, $shareId) { // Members of the new group $group = $this->groupManager->get($shareWith); if (!($group instanceof IGroup)) { return; } // User performing the share $this->shareNotificationForSharer('shared_group_self', $shareWith, $fileSource, $itemType); if ($this->currentUser->getUID() !== null) { $this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_group_by', $shareWith, $fileSource, $itemType); } $offset = 0; $users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset); while (!empty($users)) { $this->addNotificationsForGroupUsers($users, 'shared_with_by', $fileSource, $itemType, $fileTarget, $shareId); $offset += self::USER_BATCH_SIZE; $users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset); } } /** * Sharing a file or folder via link/public * * @param int $fileSource File ID that is being shared * @param string $itemType File type that is being shared (file or folder) * @param string $linkOwner */ protected function shareByLink($fileSource, $itemType, $linkOwner) { $this->view->chroot('/' . $linkOwner . '/files'); try { $path = $this->view->getPath($fileSource); } catch (NotFoundException $e) { return; } $this->shareNotificationForOriginalOwners($linkOwner, 'reshared_link_by', '', $fileSource, $itemType); $this->addNotificationsForUser( $linkOwner, 'shared_link_self', [[$fileSource => $path]], (int) $fileSource, $path, $itemType === 'file', $this->userSettings->getUserSetting($linkOwner, 'stream', Files_Sharing::TYPE_SHARED), $this->userSettings->getUserSetting($linkOwner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($linkOwner, 'setting', 'batchtime') : 0 ); } /** * Manage unsharing events * @param IShare $share * @throws \OCP\Files\NotFoundException */ public function unShare(IShare $share) { if (in_array($share->getNodeType(), ['file', 'folder'], true)) { if ($share->getShareType() === Share::SHARE_TYPE_USER) { $this->unshareFromUser($share); } else if ($share->getShareType() === Share::SHARE_TYPE_GROUP) { $this->unshareFromGroup($share); } else if ($share->getShareType() === Share::SHARE_TYPE_LINK) { $this->unshareLink($share); } } } /** * Unharing a file or folder from a user * * @param IShare $share * @throws \OCP\Files\NotFoundException */ protected function unshareFromUser(IShare $share) { // User performing the share $this->shareNotificationForSharer('unshared_user_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType()); // Owner if ($this->currentUser->getUID() !== null) { $this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_user_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType()); } // Recipient $this->addNotificationsForUser( $share->getSharedWith(), 'unshared_by', [[$share->getNodeId() => $share->getTarget()], $this->currentUser->getUserIdentifier()], $share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file', $this->userSettings->getUserSetting($share->getSharedWith(), 'stream', Files_Sharing::TYPE_SHARED), $this->userSettings->getUserSetting($share->getSharedWith(), 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($share->getSharedWith(), 'setting', 'batchtime') : 0 ); } /** * Unsharing a file or folder from a group * * @param IShare $share * @throws \OCP\Files\NotFoundException */ protected function unshareFromGroup(IShare $share) { // Members of the new group $group = $this->groupManager->get($share->getSharedWith()); if (!($group instanceof IGroup)) { return; } // User performing the share $this->shareNotificationForSharer('unshared_group_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType()); if ($this->currentUser->getUID() !== null) { $this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_group_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType()); } $offset = 0; $users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset); while (!empty($users)) { $this->addNotificationsForGroupUsers($users, 'unshared_by', $share->getNodeId(), $share->getNodeType(), $share->getTarget(), $share->getId()); $offset += self::USER_BATCH_SIZE; $users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset); } } /** * Sharing a file or folder via link/public * * @param IShare $share * @throws \OCP\Files\NotFoundException */ protected function unshareLink(IShare $share) { $owner = $share->getSharedBy(); if ($this->currentUser->getUID() === null) { // Link expired $actionSharer = 'link_expired'; $actionOwner = 'link_by_expired'; } else { $actionSharer = 'unshared_link_self'; $actionOwner = 'unshared_link_by'; } $this->addNotificationsForUser( $owner, $actionSharer, [[$share->getNodeId() => $share->getTarget()]], $share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file', $this->userSettings->getUserSetting($owner, 'stream', Files_Sharing::TYPE_SHARED), $this->userSettings->getUserSetting($owner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($owner, 'setting', 'batchtime') : 0 ); if ($share->getSharedBy() !== $share->getShareOwner()) { $owner = $share->getShareOwner(); $this->addNotificationsForUser( $owner, $actionOwner, [[$share->getNodeId() => $share->getTarget()], $share->getSharedBy()], $share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file', $this->userSettings->getUserSetting($owner, 'stream', Files_Sharing::TYPE_SHARED), $this->userSettings->getUserSetting($owner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($owner, 'setting', 'batchtime') : 0 ); } } /** * @param IUser[] $usersInGroup * @param string $actionUser * @param int $fileSource File ID that is being shared * @param string $itemType File type that is being shared (file or folder) * @param string $fileTarget File path * @param int $shareId The Share ID of this share */ protected function addNotificationsForGroupUsers(array $usersInGroup, $actionUser, $fileSource, $itemType, $fileTarget, $shareId) { $affectedUsers = []; foreach ($usersInGroup as $user) { $affectedUsers[$user->getUID()] = $fileTarget; } // Remove the triggering user, we already managed his notifications unset($affectedUsers[$this->currentUser->getUID()]); if (empty($affectedUsers)) { return; } $userIds = array_keys($affectedUsers); $filteredStreamUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'stream', Files_Sharing::TYPE_SHARED); $filteredEmailUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'email', Files_Sharing::TYPE_SHARED); $affectedUsers = $this->fixPathsForShareExceptions($affectedUsers, $shareId); foreach ($affectedUsers as $user => $path) { if (empty($filteredStreamUsersInGroup[$user]) && empty($filteredEmailUsersInGroup[$user])) { continue; } $this->addNotificationsForUser( $user, $actionUser, [[$fileSource => $path], $this->currentUser->getUserIdentifier()], $fileSource, $path, ($itemType === 'file'), !empty($filteredStreamUsersInGroup[$user]), !empty($filteredEmailUsersInGroup[$user]) ? $filteredEmailUsersInGroup[$user] : 0 ); } } /** * Check when there was a naming conflict and the target is different * for some of the users * * @param array $affectedUsers * @param int $shareId * @return mixed */ protected function fixPathsForShareExceptions(array $affectedUsers, $shareId) { $queryBuilder = $this->connection->getQueryBuilder(); $queryBuilder->select(['share_with', 'file_target']) ->from('share') ->where($queryBuilder->expr()->eq('parent', $queryBuilder->createParameter('parent'))) ->setParameter('parent', (int) $shareId); $query = $queryBuilder->execute(); while ($row = $query->fetch()) { $affectedUsers[$row['share_with']] = $row['file_target']; } return $affectedUsers; } /** * Add notifications for the user that shares a file/folder * * @param string $subject * @param string $shareWith * @param int $fileSource * @param string $itemType */ protected function shareNotificationForSharer($subject, $shareWith, $fileSource, $itemType) { $sharer = $this->currentUser->getUID(); if ($sharer === null) { return; } $this->view->chroot('/' . $sharer . '/files'); try { $path = $this->view->getPath($fileSource); } catch (NotFoundException $e) { return; } $this->addNotificationsForUser( $sharer, $subject, [[$fileSource => $path], $shareWith], $fileSource, $path, ($itemType === 'file'), $this->userSettings->getUserSetting($sharer, 'stream', Files_Sharing::TYPE_SHARED), $this->userSettings->getUserSetting($sharer, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($sharer, 'setting', 'batchtime') : 0 ); } /** * Add notifications for the user that shares a file/folder * * @param string $owner * @param string $subject * @param string $shareWith * @param int $fileSource * @param string $itemType */ protected function reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType) { $this->view->chroot('/' . $owner . '/files'); try { $path = $this->view->getPath($fileSource); } catch (NotFoundException $e) { return; } $this->addNotificationsForUser( $owner, $subject, [[$fileSource => $path], $this->currentUser->getUserIdentifier(), $shareWith], $fileSource, $path, ($itemType === 'file'), $this->userSettings->getUserSetting($owner, 'stream', Files_Sharing::TYPE_SHARED), $this->userSettings->getUserSetting($owner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($owner, 'setting', 'batchtime') : 0 ); } /** * Add notifications for the owners whose files have been reshared * * @param string $currentOwner * @param string $subject * @param string $shareWith * @param int $fileSource * @param string $itemType */ protected function shareNotificationForOriginalOwners($currentOwner, $subject, $shareWith, $fileSource, $itemType) { // Get the full path of the current user $this->view->chroot('/' . $currentOwner . '/files'); try { $path = $this->view->getPath($fileSource); } catch (NotFoundException $e) { return; } /** * Get the original owner and his path */ $owner = $this->view->getOwner($path); if ($owner !== $currentOwner) { $this->reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType); } /** * Get the sharee who shared the item with the currentUser */ $this->view->chroot('/' . $currentOwner . '/files'); $mount = $this->view->getMount($path); if (!($mount instanceof IMountPoint)) { return; } $storage = $mount->getStorage(); if (!$storage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) { return; } /** @var \OCA\Files_Sharing\SharedStorage $storage */ $shareOwner = $storage->getSharedFrom(); if ($shareOwner === '' || $shareOwner === null || $shareOwner === $owner || $shareOwner === $currentOwner) { return; } $this->reshareNotificationForSharer($shareOwner, $subject, $shareWith, $fileSource, $itemType); } /** * Adds the activity and email for a user when the settings require it * * @param string $user * @param string $subject * @param array $subjectParams * @param int $fileId * @param string $path * @param bool $isFile If the item is a file, we link to the parent directory * @param bool $streamSetting * @param int $emailSetting * @param string $type */ protected function addNotificationsForUser($user, $subject, $subjectParams, $fileId, $path, $isFile, $streamSetting, $emailSetting, $type = Files_Sharing::TYPE_SHARED) { if (!$streamSetting && !$emailSetting) { return; } $selfAction = $user === $this->currentUser->getUID(); $app = $type === Files_Sharing::TYPE_SHARED ? 'files_sharing' : 'files'; $link = $this->urlGenerator->linkToRouteAbsolute('files.view.index', array( 'dir' => ($isFile) ? dirname($path) : $path, )); $objectType = ($fileId) ? 'files' : ''; $event = $this->manager->generateEvent(); try { $event->setApp($app) ->setType($type) ->setAffectedUser($user) ->setTimestamp(time()) ->setSubject($subject, $subjectParams) ->setObject($objectType, $fileId, $path) ->setLink($link); if ($this->currentUser->getUID() !== null) { // Allow this to be empty for guests $event->setAuthor($this->currentUser->getUID()); } } catch (\InvalidArgumentException $e) { $this->logger->logException($e); } // Add activity to stream if ($streamSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'self'))) { $this->activityData->send($event); } // Add activity to mail queue if ($emailSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'selfemail'))) { $latestSend = time() + $emailSetting; $this->activityData->storeMail($event, $latestSend); } } } Parameter/Factory.php 0000604 00000007240 15247130324 0010604 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Parameter; use OCA\Activity\CurrentUser; use OCA\Activity\Formatter\IFormatter; use OCA\Activity\Formatter\BaseFormatter; use OCA\Activity\Formatter\CloudIDFormatter; use OCA\Activity\Formatter\FileFormatter; use OCA\Activity\Formatter\UserFormatter; use OCA\Activity\ViewInfoCache; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\Contacts\IManager as IContactsManager; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUserManager; class Factory { /** @var IManager */ protected $activityManager; /** @var IUserManager */ protected $userManager; /** @var IContactsManager */ protected $contactsManager; /** @var IL10N */ protected $l; /** @var ViewInfoCache */ protected $infoCache; /** @var string */ protected $user; /** @var IURLGenerator */ protected $urlGenerator; /** * @param IManager $activityManager * @param IUserManager $userManager * @param IURLGenerator $urlGenerator * @param IContactsManager $contactsManager * @param ViewInfoCache $infoCache, * @param IL10N $l * @param CurrentUser $currentUser */ public function __construct(IManager $activityManager, IUserManager $userManager, IURLGenerator $urlGenerator, IContactsManager $contactsManager, ViewInfoCache $infoCache, IL10N $l, CurrentUser $currentUser) { $this->activityManager = $activityManager; $this->userManager = $userManager; $this->urlGenerator = $urlGenerator; $this->contactsManager = $contactsManager; $this->infoCache = $infoCache; $this->l = $l; $this->user = (string) $currentUser->getUID(); } /** * @param string $user */ public function setUser($user) { $this->user = (string) $user; } /** * @param IL10N $l */ public function setL10n(IL10N $l) { $this->l = $l; } /** * @param string $parameter * @param IEvent $event * @param string $formatter * @return IParameter */ public function get($parameter, IEvent $event, $formatter) { return new Parameter( $parameter, $event, $this->getFormatter($formatter), $formatter ); } /** * @return Collection */ public function createCollection() { return new Collection($this->l, sha1(microtime() . mt_rand())); } /** * @param string $formatter * @return IFormatter */ protected function getFormatter($formatter) { switch ($formatter) { case 'file': /** @var \OCA\Activity\Formatter\FileFormatter $fileFormatter */ $fileFormatter = \OC::$server->query(FileFormatter::class); $fileFormatter->setUser($this->user); return $fileFormatter; case 'username': /** @var \OCA\Activity\Formatter\UserFormatter */ return \OC::$server->query(UserFormatter::class); case 'federated_cloud_id': /** @var \OCA\Activity\Formatter\CloudIDFormatter */ return \OC::$server->query(CloudIDFormatter::class); default: /** @var \OCA\Activity\Formatter\BaseFormatter */ return \OC::$server->query(BaseFormatter::class); } } } Parameter/IParameter.php 0000604 00000002162 15247130324 0011224 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Parameter; interface IParameter { /** * A value that is used to check, if the parameter is already in a Collection * @return mixed */ public function getParameter(); /** * @return array With two entries: value and type */ public function getParameterInfo(); /** * @return string The formatted parameter */ public function format(); } Parameter/Parameter.php 0000604 00000003707 15247130324 0011121 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Parameter; use OCA\Activity\Formatter\IFormatter; use OCP\Activity\IEvent; class Parameter implements IParameter { /** @var IFormatter */ protected $formatter; /** @var mixed */ protected $parameter; /** @var IEvent */ protected $event; /** @var string */ protected $type; /** * @param mixed $parameter * @param IEvent $event * @param IFormatter $formatter * @param string $type */ public function __construct($parameter, IEvent $event, IFormatter $formatter, $type) { $this->parameter = $parameter; $this->event = $event; $this->formatter = $formatter; $this->type = $type; } /** * @return mixed */ public function getParameter() { if ($this->event->getObjectType() && $this->event->getObjectId()) { return $this->event->getObjectType() . '#' . $this->event->getObjectId(); } return $this->parameter; } /** * @return array With two entries: value and type */ public function getParameterInfo() { return [ 'value' => $this->parameter, 'type' => $this->type, ]; } /** * @return string The formatted parameter */ public function format() { return $this->formatter->format($this->event, $this->parameter); } } Parameter/Collection.php 0000604 00000004130 15247130324 0011263 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Parameter; use OCP\IL10N; class Collection implements IParameter { /** @var IL10N */ protected $l; /** @var Parameter[] */ protected $parameters; /** @var string */ protected $random; /** * @param IL10N $l * @param string $random */ public function __construct(IL10N $l, $random) { $this->l = $l; $this->random = $random; $this->parameters = []; } /** * @param IParameter $parameter */ public function addParameter(IParameter $parameter) { foreach ($this->parameters as $existingParameter) { if ($existingParameter->getParameterInfo() === $parameter->getParameterInfo()) { return; } } $this->parameters[] = $parameter; } /** * @return mixed */ public function getParameter() { return $this->random; } /** * @return array With two entries: value and type */ public function getParameterInfo() { $parameters = []; foreach ($this->parameters as $parameter) { $parameters[] = $parameter->getParameterInfo(); } return [ 'value' => $parameters, 'type' => 'collection', ]; } /** * @return string The formatted parameter */ public function format() { $parameterList = $plainParameterList = []; foreach ($this->parameters as $parameter) { $parameterList[] = $parameter->format(); } return '<collection>' . implode('', $parameterList) . '</collection>'; } } Navigation.php 0000604 00000006500 15247130324 0007352 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCP\Activity\IFilter; use OCP\Activity\IManager; use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; use OCP\Template; /** * Class Navigation * * @package OCA\Activity */ class Navigation { /** @var IL10N */ protected $l; /** @var IManager */ protected $activityManager; /** @var IURLGenerator */ protected $URLGenerator; /** @var IConfig */ protected $config; /** @var CurrentUser */ protected $currentUser; /** * Construct * * @param IL10N $l * @param IManager $manager * @param IURLGenerator $URLGenerator * @param IConfig $config * @param CurrentUser $currentUser */ public function __construct(IL10N $l, IManager $manager, IURLGenerator $URLGenerator, IConfig $config, CurrentUser $currentUser) { $this->l = $l; $this->activityManager = $manager; $this->URLGenerator = $URLGenerator; $this->config = $config; $this->currentUser = $currentUser; } /** * Get the users we want to send an email to * * @param null|string $forceActive Navigation entry that should be marked as active * @return \OCP\Template */ public function getTemplate($forceActive = 'all') { $active = $forceActive ?: 'all'; $template = new Template('activity', 'stream.app.navigation', ''); $template->assign('activeNavigation', $active); $template->assign('navigations', $this->getLinkList()); $template->assign('rssLink', $this->getRSSLink()); return $template; } /** * @return string */ protected function getRSSLink() { $rssToken = $this->config->getUserValue($this->currentUser->getUID(), 'activity', 'rsstoken'); if ($rssToken) { return $this->URLGenerator->linkToRouteAbsolute('activity.Feed.show', array('token' => $rssToken)); } else { return ''; } } /** * Get all items for the users we want to send an email to * * @return array Notification data (user => array of rows from the table) */ public function getLinkList() { $filters = $this->activityManager->getFilters(); usort($filters, function(IFilter $a, IFilter $b) { if ($a->getPriority() === $b->getPriority()) { return $a->getIdentifier() > $b->getIdentifier(); } return $a->getPriority() > $b->getPriority(); }); $entries = []; foreach ($filters as $filter) { $entries[] = [ 'id' => $filter->getIdentifier(), 'icon' => $filter->getIcon(), 'name' => $filter->getName(), 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', array('filter' => $filter->getIdentifier())), ]; } return $entries; } } Exception/InvalidFilterException.php 0000604 00000001533 15247130324 0013625 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Exception; class InvalidFilterException extends \InvalidArgumentException { } GroupHelper.php 0000604 00000013002 15247130324 0007502 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCA\Activity\Extension\LegacyParser; use OCA\Activity\Parameter\IParameter; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\IL10N; class GroupHelper { /** @var IEvent[] */ protected $event = []; /** @var int */ protected $lastEvent = 0; /** @var bool */ protected $allowGrouping; /** @var IL10N */ protected $l; /** @var \OCP\Activity\IManager */ protected $activityManager; /** @var \OCA\Activity\DataHelper */ protected $dataHelper; /** @var LegacyParser */ protected $legacyParser; /** * @param IL10N $l * @param \OCP\Activity\IManager $activityManager * @param \OCA\Activity\DataHelper $dataHelper * @param LegacyParser $legacyParser */ public function __construct(IL10N $l, IManager $activityManager, DataHelper $dataHelper, LegacyParser $legacyParser) { $this->allowGrouping = true; $this->l = $l; $this->activityManager = $activityManager; $this->dataHelper = $dataHelper; $this->legacyParser = $legacyParser; } /** * @param string $user */ public function setUser($user) { $this->dataHelper->setUser($user); } /** * @param IL10N $l */ public function setL10n(IL10N $l) { $this->l = $l; $this->dataHelper->setL10n($l); } /** * Add an activity to the internal array * * @param array $activity */ public function addActivity($activity) { $id = (int) $activity['activity_id']; $event = $this->arrayToEvent($activity); $language = $this->l->getLanguageCode(); foreach ($this->activityManager->getProviders() as $provider) { try { $this->activityManager->setFormattingObject($event->getObjectType(), $event->getObjectId()); if ($this->allowGrouping && $this->lastEvent !== 0 && isset($this->event[$this->lastEvent])) { $event = $provider->parse($language, $event, $this->event[$this->lastEvent]); } else { $event = $provider->parse($language, $event); } $this->activityManager->setFormattingObject('', 0); $child = $event->getChildEvent(); if ($child instanceof IEvent) { unset($this->event[$this->lastEvent]); } } catch (\InvalidArgumentException $e) { } } if (!$event->getParsedSubject()) { try { $this->activityManager->setFormattingObject($event->getObjectType(), $event->getObjectId()); $event = $this->legacyParser->parse($language, $event); $this->activityManager->setFormattingObject('', 0); } catch (\InvalidArgumentException $e) { \OC::$server->getLogger()->debug('Failed to parse activity'); return; } } $this->event[$id] = $event; $this->lastEvent = $id; } /** * Get the prepared activities * * @return array translated activities ready for use */ public function getActivities() { $return = []; foreach ($this->event as $id => $event) { $return[] = $this->eventToArray($event, $id); } $this->event = []; return $return; } /** * @param array $row * @return IEvent */ protected function arrayToEvent(array $row) { $event = $this->activityManager->generateEvent(); $event->setApp((string) $row['app']) ->setType((string) $row['type']) ->setAffectedUser((string) $row['affecteduser']) ->setAuthor((string) $row['user']) ->setTimestamp((int) $row['timestamp']) ->setSubject((string) $row['subject'], json_decode($row['subjectparams'], true)) ->setMessage((string) $row['message'], json_decode($row['messageparams'], true)) ->setObject((string) $row['object_type'], (int) $row['object_id'], (string) $row['file']) ->setLink((string) $row['link']); return $event; } /** * @param IEvent $event * @return array */ protected function eventToArray(IEvent $event, $id) { return [ 'activity_id' => $id, 'app' => $event->getApp(), 'type' => $event->getType(), 'affecteduser' => $event->getAffectedUser(), 'user' => $event->getAuthor(), 'timestamp' => $event->getTimestamp(), 'subject' => $event->getParsedSubject(), 'subject_rich' => [ (string) $event->getRichSubject(), (array) $event->getRichSubjectParameters(), ], 'message' => $event->getParsedMessage(), 'message_rich' => [ (string) $event->getRichMessage(), (array) $event->getRichMessageParameters(), ], 'object_type' => $event->getObjectType(), 'object_id' => $event->getObjectId(), 'object_name' => $event->getObjectName(), 'objects' => $this->getObjectsFromChildren($event), 'link' => $event->getLink(), 'icon' => $event->getIcon(), ]; } /** * @param IEvent $event * @return array */ protected function getObjectsFromChildren(IEvent $event) { $child = $event->getChildEvent(); if ($child instanceof IEvent) { $objects = $this->getObjectsFromChildren($child); $objects[$event->getObjectId()] = $event->getObjectName(); return $objects; } else { return [$event->getObjectId() => $event->getObjectName()]; } } } Consumer.php 0000604 00000005327 15247130324 0007054 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCP\Activity\IConsumer; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\L10N\IFactory; class Consumer implements IConsumer { /** @var Data */ protected $data; /** @var IManager */ protected $manager; /** @var UserSettings */ protected $userSettings; /** @var IFactory */ protected $l10nFactory; /** * Constructor * * @param Data $data * @param IManager $manager * @param UserSettings $userSettings * @param IFactory $l10nFactory */ public function __construct(Data $data, IManager $manager, UserSettings $userSettings, IFactory $l10nFactory) { $this->data = $data; $this->manager = $manager; $this->userSettings = $userSettings; $this->l10nFactory = $l10nFactory; } /** * Send an event to the notifications of a user * * @param IEvent $event * @return null */ public function receive(IEvent $event) { $selfAction = $event->getAffectedUser() === $event->getAuthor(); $streamSetting = $this->userSettings->getUserSetting($event->getAffectedUser(), 'stream', $event->getType()); $emailSetting = $this->userSettings->getUserSetting($event->getAffectedUser(), 'email', $event->getType()); $emailSetting = ($emailSetting) ? $this->userSettings->getUserSetting($event->getAffectedUser(), 'setting', 'batchtime') : false; // User is not the author or wants to see their own actions $createStream = !$selfAction || $this->userSettings->getUserSetting($event->getAffectedUser(), 'setting', 'self'); // Add activity to stream if ($streamSetting && $createStream) { $this->data->send($event); } // User is not the author or wants to see their own actions $createEmail = !$selfAction || $this->userSettings->getUserSetting($event->getAffectedUser(), 'setting', 'selfemail'); // Add activity to mail queue if ($emailSetting && $createEmail) { $latestSend = $event->getTimestamp() + $emailSetting; $this->data->storeMail($event, $latestSend); } } } UserSettings.php 0000604 00000012321 15247130324 0007710 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCP\Activity\IManager; use OCP\IConfig; /** * Class UserSettings * * @package OCA\Activity */ class UserSettings { /** @var IManager */ protected $manager; /** @var IConfig */ protected $config; /** @var Data */ protected $data; const EMAIL_SEND_HOURLY = 0; const EMAIL_SEND_DAILY = 1; const EMAIL_SEND_WEEKLY = 2; /** * @param IManager $manager * @param IConfig $config */ public function __construct(IManager $manager, IConfig $config) { $this->manager = $manager; $this->config = $config; } /** * Get a setting for a user * * Falls back to some good default values if the user does not have a preference * * @param string $user * @param string $method Should be one of 'stream', 'email' or 'setting' * @param string $type One of the activity types, 'batchtime' or 'self' * @return bool|int */ public function getUserSetting($user, $method, $type) { $defaultSetting = $this->getDefaultFromSetting($method, $type); if (is_bool($defaultSetting)) { return (bool) $this->config->getUserValue( $user, 'activity', 'notify_' . $method . '_' . $type, $defaultSetting ); } return (int) $this->config->getUserValue( $user, 'activity', 'notify_' . $method . '_' . $type, $defaultSetting ); } /** * @param string $method * @param string $type * @return bool|int */ public function getConfigSetting($method, $type) { $defaultSetting = $this->getDefaultFromSetting($method, $type); if (is_bool($defaultSetting)) { return (bool) $this->config->getAppValue( 'activity', 'notify_' . $method . '_' . $type, $defaultSetting ); } return (int) $this->config->getAppValue( 'activity', 'notify_' . $method . '_' . $type, $defaultSetting ); } /** * Get a good default setting for a preference * * @param string $method Should be one of 'stream', 'email' or 'setting' * @param string $type One of the activity types, 'batchtime', 'self' or 'selfemail' * @return bool|int */ protected function getDefaultFromSetting($method, $type) { if ($method === 'setting') { if ($type === 'batchtime') { return 3600; } if ($type === 'self') { return true; } if ($type === 'selfemail') { return false; } return false; } try { $setting = $this->manager->getSettingById($type); return ($method === 'stream') ? $setting->isDefaultEnabledStream() : $setting->isDefaultEnabledMail(); } catch (\InvalidArgumentException $e) { return false; } } /** * Get a list with enabled notification types for a user * * @param string $user Name of the user * @param string $method Should be one of 'stream' or 'email' * @return array */ public function getNotificationTypes($user, $method) { $notificationTypes = array(); $settings = $this->manager->getSettings(); foreach ($settings as $setting) { if ($this->getUserSetting($user, $method, $setting->getIdentifier())) { $notificationTypes[] = $setting->getIdentifier(); } } return $notificationTypes; } /** * Filters the given user array by their notification setting * * @param array $users * @param string $method * @param string $type * @return array Returns a "username => b:true" Map for method = stream * Returns a "username => i:batchtime" Map for method = email */ public function filterUsersBySetting($users, $method, $type) { if (empty($users) || !is_array($users)) { return array(); } $filteredUsers = array(); $potentialUsers = $this->config->getUserValueForUsers('activity', 'notify_' . $method . '_' . $type, $users); foreach ($potentialUsers as $user => $value) { if ($value) { $filteredUsers[$user] = true; } unset($users[array_search($user, $users, true)]); } // Get the batch time setting from the database if ($method === 'email') { $potentialUsers = $this->config->getUserValueForUsers('activity', 'notify_setting_batchtime', array_keys($filteredUsers)); foreach ($potentialUsers as $user => $value) { $filteredUsers[$user] = $value; } } if (empty($users)) { return $filteredUsers; } // If the setting is enabled by default, // we add all users that didn't set the preference yet. if ($this->getDefaultFromSetting($method, $type)) { foreach ($users as $user) { if ($method === 'stream') { $filteredUsers[$user] = true; } else { $filteredUsers[$user] = $this->getDefaultFromSetting('setting', 'batchtime'); } } } return $filteredUsers; } } DataHelper.php 0000604 00000013161 15247130324 0007265 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCA\Activity\Parameter\Factory; use OCA\Activity\Parameter\IParameter; use OCA\Activity\Parameter\Collection; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\IL10N; use OCP\L10N\IFactory; class DataHelper { /** @var \OCP\Activity\IManager */ protected $activityManager; /** @var \OCA\Activity\Parameter\Factory */ protected $parameterFactory; /** @var IFactory */ protected $l10Nfactory; /** @var IL10N */ protected $l; /** * @param IManager $activityManager * @param Factory $parameterFactory * @param IFactory $l10Nfactory * @param IL10N $l */ public function __construct(IManager $activityManager, Factory $parameterFactory, IFactory $l10Nfactory, IL10N $l) { $this->activityManager = $activityManager; $this->parameterFactory = $parameterFactory; $this->l10Nfactory = $l10Nfactory; $this->l = $l; } /** * @param string $user */ public function setUser($user) { $this->parameterFactory->setUser($user); } /** * @param IL10N $l */ public function setL10n(IL10N $l) { $this->parameterFactory->setL10n($l); $this->l = $l; } /** * @brief Translate an event string with the translations from the app where it was send from * @param string $app The app where this event comes from * @param string $text The text including placeholders * @param IParameter[] $params The parameter for the placeholder * @return string translated */ public function translation($app, $text, array $params) { if (!$text) { return ''; } $preparedParams = []; foreach ($params as $parameter) { $preparedParams[] = $parameter->format(); } // Allow apps to correctly translate their activities $translation = $this->activityManager->translate( $app, $text, $preparedParams, false, false, $this->l->getLanguageCode()); if ($translation !== false) { return $translation; } $l = $this->l10Nfactory->get($app, $this->l->getLanguageCode()); return $l->t($text, $preparedParams); } /** * List with special parameters for the message * * @param string $app * @param string $text * @return array */ protected function getSpecialParameterList($app, $text) { $specialParameters = $this->activityManager->getSpecialParameterList($app, $text); if ($specialParameters !== false) { return $specialParameters; } return array(); } /** * Format strings for display * * @param array $activity * @param string $message 'subject' or 'message' * @return array Modified $activity */ public function formatStrings($activity, $message) { $activity[$message . 'params'] = $activity[$message . 'params_array']; unset($activity[$message . 'params_array']); $activity[$message . '_prepared'] = $this->translation($activity['app'], $activity[$message], $activity[$message . 'params']); return $activity; } /** * Get the parameter array from the parameter string of the database table * * @param IEvent $event * @param string $parsing What are we parsing `message` or `subject` * @param string $parameterString can be a JSON string, serialize() or a simple string. * @return array List of Parameters */ public function getParameters(IEvent $event, $parsing, $parameterString) { $parameters = $this->parseParameters($parameterString); $parameterTypes = $this->getSpecialParameterList( $event->getApp(), ($parsing === 'subject') ? $event->getSubject() : $event->getMessage() ); foreach ($parameters as $i => $parameter) { $parameters[$i] = $this->parameterFactory->get( $parameter, $event, isset($parameterTypes[$i]) ? $parameterTypes[$i] : 'base' ); } return $parameters; } /** * @return Collection */ public function createCollection() { return $this->parameterFactory->createCollection(); } /** * Get the parameter array from the parameter string of the database table * * @param string $parameterString can be a JSON string, serialize() or a simple string. * @return array List of Parameters */ public function parseParameters($parameterString) { if (!is_string($parameterString)) { return []; } $parameters = $parameterString; if ($parameterString[0] === '[' && substr($parameterString, -1) === ']' || $parameterString[0] === '"' && substr($parameterString, -1) === '"') { // ownCloud 8.1+ $parameters = json_decode($parameterString, true); if ($parameters === null) { // Error on json decode $parameters = $parameterString; } } else if (isset($parameterString[7]) && $parameterString[1] === ':' && ($parameterString[0] === 's' && substr($parameterString, -1) === ';' || $parameterString[0] === 'a' && substr($parameterString, -1) === '}')) { // ownCloud 7+ // Min length 8: `s:1:"a";` // Accepts: `s:1:"a";` for single string `a:1:{i:0;s:1:"a";}` for array $parameters = unserialize($parameterString); } if (is_array($parameters)) { return $parameters; } // ownCloud <7 return [$parameters]; } } Filter/ByFilter.php 0000604 00000004145 15247130324 0010223 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity\Filter; use OCP\Activity\IFilter; use OCP\IL10N; use OCP\IURLGenerator; class ByFilter implements IFilter { /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** * @param IL10N $l * @param IURLGenerator $url */ public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * @return string Lowercase a-z only identifier * @since 9.2.0 */ public function getIdentifier() { return 'by'; } /** * @return string A translated string * @since 9.2.0 */ public function getName() { return $this->l->t('By others'); } /** * @return int * @since 9.2.0 */ public function getPriority() { return 2; } /** * @return string Full URL to an icon, empty string when none is given * @since 9.2.0 */ public function getIcon() { return $this->url->getAbsoluteURL($this->url->imagePath('core', 'places/contacts-dark.svg')); } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 9.2.0 */ public function filterTypes(array $types) { return $types; } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 9.2.0 */ public function allowedApps() { return []; } } Filter/SelfFilter.php 0000604 00000004136 15247130324 0010542 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity\Filter; use OCP\Activity\IFilter; use OCP\IL10N; use OCP\IURLGenerator; class SelfFilter implements IFilter { /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** * @param IL10N $l * @param IURLGenerator $url */ public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * @return string Lowercase a-z only identifier * @since 9.2.0 */ public function getIdentifier() { return 'self'; } /** * @return string A translated string * @since 9.2.0 */ public function getName() { return $this->l->t('By you'); } /** * @return int * @since 9.2.0 */ public function getPriority() { return 1; } /** * @return string Full URL to an icon, empty string when none is given * @since 9.2.0 */ public function getIcon() { return $this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/user.svg')); } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 9.2.0 */ public function filterTypes(array $types) { return $types; } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 9.2.0 */ public function allowedApps() { return []; } } Filter/AllFilter.php 0000604 00000004151 15247130324 0010356 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity\Filter; use OCP\Activity\IFilter; use OCP\IL10N; use OCP\IURLGenerator; class AllFilter implements IFilter { /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** * @param IL10N $l * @param IURLGenerator $url */ public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * @return string Lowercase a-z only identifier * @since 9.2.0 */ public function getIdentifier() { return 'all'; } /** * @return string A translated string * @since 9.2.0 */ public function getName() { return $this->l->t('All activities'); } /** * @return int * @since 9.2.0 */ public function getPriority() { return 0; } /** * @return string Full URL to an icon, empty string when none is given * @since 9.2.0 */ public function getIcon() { return $this->url->getAbsoluteURL($this->url->imagePath('activity', 'activity-dark.svg')); } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 9.2.0 */ public function filterTypes(array $types) { return $types; } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 9.2.0 */ public function allowedApps() { return []; } } BackgroundJob/ExpireActivities.php 0000604 00000002735 15247130324 0013254 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\BackgroundJob; use OC\BackgroundJob\TimedJob; use OCA\Activity\Data; use OCP\IConfig; /** * Class ExpireActivities * * @package OCA\Activity\BackgroundJob */ class ExpireActivities extends TimedJob { /** @var Data */ protected $data; /** @var IConfig */ protected $config; /** * @param Data $data * @param IConfig $config */ public function __construct(Data $data, IConfig $config) { // Run once per day $this->setInterval(60 * 60 * 24); $this->data = $data; $this->config = $config; } protected function run($argument) { // Remove activities that are older then one year $expireDays = $this->config->getSystemValue('activity_expire_days', 365); $this->data->expire($expireDays); } } BackgroundJob/EmailNotification.php 0000604 00000004100 15247130324 0013355 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\BackgroundJob; use OC\BackgroundJob\TimedJob; use OCA\Activity\MailQueueHandler; /** * Class EmailNotification * * @package OCA\Activity\BackgroundJob */ class EmailNotification extends TimedJob { /** @var MailQueueHandler */ protected $queueHandler; /** @var bool */ protected $isCLI; /** * @param MailQueueHandler $mailQueueHandler * @param bool $isCLI */ public function __construct(MailQueueHandler $mailQueueHandler, $isCLI) { // Run all 15 Minutes $this->setInterval(15 * 60); $this->queueHandler = $mailQueueHandler; $this->isCLI = $isCLI; } protected function run($argument) { // We don't use time() but "time() - 1" here, so we don't run into // runtime issues later and delete emails, which were created in the // same second, but were not collected for the emails. $sendTime = time() - 1; if ($this->isCLI) { do { // If we are in CLI mode, we keep sending emails // until we are done. $emails_sent = $this->queueHandler->sendEmails(MailQueueHandler::CLI_EMAIL_BATCH_SIZE, $sendTime); } while ($emails_sent === MailQueueHandler::CLI_EMAIL_BATCH_SIZE); } else { // Only send 25 Emails in one go for web cron $this->queueHandler->sendEmails(MailQueueHandler::WEB_EMAIL_BATCH_SIZE, $sendTime); } } } BackgroundJob/RemoteActivity.php 0000604 00000006443 15247130324 0012743 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity\BackgroundJob; use GuzzleHttp\Exception\ClientException; use OC\BackgroundJob\QueuedJob; use OCA\Activity\Extension\Files; use OCP\Federation\ICloudId; use OCP\Federation\ICloudIdManager; use OCP\Http\Client\IClientService; class RemoteActivity extends QueuedJob { /** @var IClientService */ protected $clientService; /** @var ICloudIdManager */ protected $cloudIdManager; public function __construct(IClientService $clientService, ICloudIdManager $cloudIdManager) { $this->clientService = $clientService; $this->cloudIdManager = $cloudIdManager; } protected function run($arguments) { call_user_func_array([$this, 'sendActivity'], $arguments); } protected function sendActivity($target, $token, $path, $internalType, $time, $actor, $secondPath = '') { $client = $this->clientService->newClient(); $cloudId = $this->cloudIdManager->resolveCloudId($target); $type = $this->translateType($internalType, $secondPath); $fields = [ '@context' => 'https://www.w3.org/ns/activitystreams', 'to' => [ 'type' => 'Person', 'name' => $cloudId->getUser(), ], 'actor' => [ 'type' => 'Person', 'name' => $actor, ], 'type' => $type, 'updated' => date(\DateTime::W3C, $time), ]; if ($type === 'Move') { $fields['target'] = [ 'type' => 'Document', 'name' => $path, ]; $fields['origin'] = [ 'type' => 'Document', 'name' => $secondPath, ]; } else { $fields['object'] = [ 'type' => 'Document', 'name' => $path, ]; } try { $client->post( $this->getServerURL($cloudId, $token), [ 'body' => $fields, 'timeout' => 10, 'connect_timeout' => 10, ] ); } catch (ClientException $e) { } } /** * @param ICloudId $cloudId * @param string $token * @return string */ protected function getServerURL(ICloudId $cloudId, $token) { $remote = $cloudId->getRemote(); if (strpos($remote, 'http') !== 0) { $remote = 'https://' . $remote; } return rtrim($remote, '/') . '/ocs/v2.php/apps/activity/api/v2/remote/' . $token; } /** * @param string $internalType * @param string $secondPath * @return string */ protected function translateType($internalType, $secondPath) { switch ($internalType) { case Files::TYPE_SHARE_CREATED: case Files::TYPE_SHARE_RESTORED: return 'Create'; case Files::TYPE_SHARE_CHANGED: if ($secondPath !== '') { return 'Move'; } return 'Update'; case Files::TYPE_SHARE_DELETED: return 'Delete'; } return ''; } } GroupHelperDisabled.php 0000604 00000002474 15247130324 0011145 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity; use OCA\Activity\Extension\LegacyParser; use OCP\Activity\IManager; use OCP\IL10N; class GroupHelperDisabled extends GroupHelper { /** * @param IL10N $l * @param IManager $activityManager * @param DataHelper $dataHelper * @param LegacyParser $legacyParser */ public function __construct(IL10N $l, IManager $activityManager, DataHelper $dataHelper, LegacyParser $legacyParser) { parent::__construct($l, $activityManager, $dataHelper, $legacyParser); $this->allowGrouping = false; } } Formatter/BaseFormatter.php 0000604 00000002170 15247130324 0011753 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Formatter; use OCP\Activity\IEvent; use OCP\Util; class BaseFormatter implements IFormatter { /** * @param IEvent $event * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ public function format(IEvent $event, $parameter) { return '<parameter>' . Util::sanitizeHTML($parameter) . '</parameter>'; } } Formatter/UserFormatter.php 0000604 00000005031 15247130324 0012016 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Formatter; use OCP\Activity\IEvent; use OCP\IL10N; use OCP\IUser; use OCP\IUserManager; use OCP\Util; class UserFormatter implements IFormatter { /** @var IUserManager */ protected $manager; /** @var IL10N */ protected $l; /** @var CloudIDFormatter */ protected $cloudIDFormatter; /** * @param IUserManager $userManager * @param CloudIDFormatter $cloudIDFormatter * @param IL10N $l */ public function __construct(IUserManager $userManager, CloudIDFormatter $cloudIDFormatter, IL10N $l) { $this->manager = $userManager; $this->l = $l; $this->cloudIDFormatter = $cloudIDFormatter; } /** * @param IEvent $event * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ public function format(IEvent $event, $parameter) { // If the username is empty, the action has been performed by a remote // user, or via a public share. We don't know the username in that case if ($parameter === '') { return '<user display-name="' . Util::sanitizeHTML($this->l->t('"remote user"')) . '">' . Util::sanitizeHTML('') . '</user>'; } $user = $this->manager->get($parameter); if (!($user instanceof IUser)) { if ($this->isRemoteUser($parameter)) { // Remote user detected return $this->cloudIDFormatter->format($event, $parameter); } $displayName = $parameter; } else { $displayName = $user->getDisplayName(); } $parameter = Util::sanitizeHTML($parameter); return '<user display-name="' . Util::sanitizeHTML($displayName) . '">' . Util::sanitizeHTML($parameter) . '</user>'; } /** * Very simple "remote user" detection should be improved someday™ * * @param string $parameter * @return bool */ protected function isRemoteUser($parameter) { return strpos($parameter, '@') > 0; } } Formatter/IFormatter.php 0000604 00000002007 15247130324 0011270 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Formatter; use OCP\Activity\IEvent; interface IFormatter { /** * @param IEvent $event * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ public function format(IEvent $event, $parameter); } Formatter/CloudIDFormatter.php 0000604 00000006001 15247130324 0012361 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Formatter; use OC\HintException; use OC\Share\Helper; use OCP\Activity\IEvent; use OCP\Contacts\IManager; use OCP\Util; class CloudIDFormatter implements IFormatter { /** @var IManager */ protected $manager; /** @var array */ protected $federatedContacts; /** * @param IManager $contactsManager */ public function __construct(IManager $contactsManager) { $this->manager = $contactsManager; $this->federatedContacts = []; } /** * @param IEvent $event * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ public function format(IEvent $event, $parameter) { $displayName = $parameter; try { list($user, $server) = Helper::splitUserRemote($parameter); } catch (HintException $e) { $user = $parameter; $server = ''; } if ($server !== '') { $displayName = $user . '@…'; } try { $displayName = $this->getDisplayNameFromContact($parameter); } catch (\OutOfBoundsException $e) {} return '<federated-cloud-id display-name="' . Util::sanitizeHTML($displayName) . '" user="' . Util::sanitizeHTML($user) . '" server="' . Util::sanitizeHTML($server) . '">' . Util::sanitizeHTML($parameter) . '</federated-cloud-id>'; } /** * Try to find the user in the contacts * * @param string $federatedCloudId * @return string * @throws \OutOfBoundsException when there is no contact for the id */ protected function getDisplayNameFromContact($federatedCloudId) { $federatedCloudId = strtolower($federatedCloudId); if (isset($this->federatedContacts[$federatedCloudId])) { if ($this->federatedContacts[$federatedCloudId] !== '') { return $this->federatedContacts[$federatedCloudId]; } else { throw new \OutOfBoundsException('No contact found for federated cloud id'); } } $addressBookEntries = $this->manager->search($federatedCloudId, ['CLOUD']); foreach ($addressBookEntries as $entry) { if (isset($entry['CLOUD'])) { foreach ($entry['CLOUD'] as $cloudID) { if ($cloudID === $federatedCloudId) { $this->federatedContacts[$federatedCloudId] = $entry['FN']; return $entry['FN']; } } } } $this->federatedContacts[$federatedCloudId] = ''; throw new \OutOfBoundsException('No contact found for federated cloud id'); } } Formatter/FileFormatter.php 0000604 00000007261 15247130324 0011766 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Formatter; use OCA\Activity\ViewInfoCache; use OCP\Activity\IEvent; use OCP\IL10N; use OCP\IURLGenerator; use OCP\Util; class FileFormatter implements IFormatter { /** @var ViewInfoCache */ protected $infoCache; /** @var IURLGenerator */ protected $urlGenerator; /** @var IL10N */ protected $l; /** @var string */ protected $user; /** * @param ViewInfoCache $infoCache * @param IURLGenerator $urlGenerator * @param IL10N $l */ public function __construct(ViewInfoCache $infoCache, IURLGenerator $urlGenerator, IL10N $l) { $this->infoCache = $infoCache; $this->urlGenerator = $urlGenerator; $this->l = $l; } /** * @param string $user */ public function setUser($user) { $this->user = (string) $user; } /** * @param IEvent $event * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ public function format(IEvent $event, $parameter) { $param = $this->fixLegacyFilename($parameter); // If the activity is about the very same file, we use the current path // for the link generation instead of the one that was saved. $fileId = ''; if (is_array($param)) { $fileId = key($param); $param = $param[$fileId]; $info = $this->infoCache->getInfoById($this->user, $fileId, $param); } elseif ($event->getObjectType() === 'files' && $event->getObjectName() === $param) { $fileId = $event->getObjectId(); $info = $this->infoCache->getInfoById($this->user, $fileId, $param); } else { $info = $this->infoCache->getInfoByPath($this->user, $param); } if ($info['is_dir']) { $linkData = ['dir' => $info['path']]; } else { $parentDir = (substr_count($info['path'], '/') === 1) ? '/' : dirname($info['path']); $fileName = basename($info['path']); $linkData = [ 'dir' => $parentDir, 'scrollto' => $fileName, ]; } if ($info['view'] !== '') { $linkData['view'] = $info['view']; } $param = trim($param, '/'); if ($param === '') { $param = '/'; } $fileLink = $this->urlGenerator->linkToRouteAbsolute('files.view.index', $linkData); return '<file link="' . $fileLink . '" id="' . Util::sanitizeHTML($fileId) . '">' . Util::sanitizeHTML($param) . '</file>'; } /** * Prepend leading slash to filenames of legacy activities * @param string|array $filename * @return string|array */ protected function fixLegacyFilename($filename) { if (is_array($filename)) { // 9.0: [fileId => path] return $filename; } if (strpos($filename, '/') !== 0) { return '/' . $filename; } return $filename; } /** * Split the path from the filename string * * @param string $filename * @return array Array with path and filename */ protected function splitPathFromFilename($filename) { if (strrpos($filename, '/') !== false) { return array( trim(substr($filename, 0, strrpos($filename, '/')), '/'), substr($filename, strrpos($filename, '/') + 1), ); } return array('', $filename); } } Command/SendEmails.php 0000604 00000006177 15247130324 0010667 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity\Command; use OCA\Activity\MailQueueHandler; use OCA\Activity\UserSettings; use OCP\IConfig; use OCP\ILogger; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class SendEmails extends Command { /** @var MailQueueHandler */ protected $queueHandler; /** @var IConfig */ protected $config; /** @var ILogger */ protected $logger; /** * @param MailQueueHandler $queueHandler * @param IConfig $config * @param ILogger $logger */ public function __construct(MailQueueHandler $queueHandler, IConfig $config, ILogger $logger) { parent::__construct(); $this->queueHandler = $queueHandler; $this->config = $config; $this->logger = $logger; } protected function configure() { $this ->setName('activity:send-mails') ->setDescription('Sends the activity notification mails') ->addArgument( 'restrict-batching', InputArgument::OPTIONAL, 'Only sends the emails for users which have configured the mails: "hourly", "daily" or "weekly"', 'all' ) ; } /** * @param InputInterface $input * @param OutputInterface $output * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { // We don't use time() but "time() - 1" here, so we don't run into // runtime issues later and delete emails, which were created in the // same second, but were not collected for the emails. $sendTime = time() - 1; $restrictBatching = $input->getArgument('restrict-batching'); if ($restrictBatching === 'hourly') { $restrictEmails = UserSettings::EMAIL_SEND_HOURLY; } else if ($restrictBatching === 'daily') { $restrictEmails = UserSettings::EMAIL_SEND_DAILY; } else if ($restrictBatching === 'weekly') { $restrictEmails = UserSettings::EMAIL_SEND_WEEKLY; } else { $restrictEmails = null; } do { // If we are in CLI mode, we keep sending emails // until we are done. $emails_sent = $this->queueHandler->sendEmails(MailQueueHandler::CLI_EMAIL_BATCH_SIZE, $sendTime, true, $restrictEmails); } while ($emails_sent === MailQueueHandler::CLI_EMAIL_BATCH_SIZE); return 0; } } ViewInfoCache.php 0000604 00000006321 15247130324 0007726 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OC\Files\View; use OCP\Files\NotFoundException; class ViewInfoCache { /** @var array */ protected $cachePath; /** @var array */ protected $cacheId; /** @var \OC\Files\View */ protected $view; /** * @param View $view */ public function __construct(View $view) { $this->view = $view; } /** * @param string $user * @param string $path * @return array */ public function getInfoByPath($user, $path) { if (isset($this->cachePath[$user][$path])) { return $this->cachePath[$user][$path]; } return $this->findInfoByPath($user, $path); } /** * @param string $user * @param int $fileId * @param string $path * @return array */ public function getInfoById($user, $fileId, $path) { if (isset($this->cacheId[$user][$fileId])) { $cache = $this->cacheId[$user][$fileId]; if ($cache['path'] === null) { $cache['path'] = $path; } return $cache; } return $this->findInfoById($user, $fileId, $path); } /** * @param string $user * @param string $path * @return array */ protected function findInfoByPath($user, $path) { $this->view->chroot('/' . $user . '/files'); $exists = $this->view->file_exists($path); $this->cachePath[$user][$path] = [ 'path' => $path, 'exists' => $exists, 'is_dir' => $exists ? $this->view->is_dir($path) : false, 'view' => '', ]; return $this->cachePath[$user][$path]; } /** * @param string $user * @param int $fileId * @param string $filePath * @return array */ protected function findInfoById($user, $fileId, $filePath) { $this->view->chroot('/' . $user . '/files'); $cache = [ 'path' => $filePath, 'exists' => false, 'is_dir' => false, 'view' => '', ]; $notFound = false; try { $path = $this->view->getPath($fileId); $cache['path'] = $path; $cache['is_dir'] = $this->view->is_dir($path); $cache['exists'] = true; } catch (NotFoundException $e) { // The file was not found in the normal view, maybe it is in // the trashbin? $this->view->chroot('/' . $user . '/files_trashbin'); try { $path = $this->view->getPath($fileId); $cache = [ 'path' => substr($path, strlen('/files')), 'exists' => true, 'is_dir' => $this->view->is_dir($path), 'view' => 'trashbin', ]; } catch (NotFoundException $e) { $notFound = true; } } $this->cacheId[$user][$fileId] = $cache; if ($notFound) { $this->cacheId[$user][$fileId]['path'] = null; } return $cache; } } FilesHooksStatic.php 0000604 00000005237 15247130324 0010477 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCP\Share\IShare; use Symfony\Component\EventDispatcher\GenericEvent; /** * The class to handle the filesystem hooks */ class FilesHooksStatic { /** * @return FilesHooks */ static protected function getHooks() { return \OC::$server->query(FilesHooks::class); } /** * Store the create hook events * @param array $params The hook params */ public static function fileCreate($params) { self::getHooks()->fileCreate($params['path']); } /** * Store the update hook events * @param array $params The hook params */ public static function fileUpdate($params) { self::getHooks()->fileUpdate($params['path']); } /** * Store the delete hook events * @param array $params The hook params */ public static function fileDelete($params) { self::getHooks()->fileDelete($params['path']); } /** * Store the rename hook events * @param array $params The hook params */ public static function fileMove($params) { self::getHooks()->fileMove($params['oldpath'], $params['newpath']); } /** * Store the rename hook events * @param array $params The hook params */ public static function fileMovePost($params) { self::getHooks()->fileMovePost($params['oldpath'], $params['newpath']); } /** * Store the restore hook events * @param array $params The hook params */ public static function fileRestore($params) { self::getHooks()->fileRestore($params['filePath']); } /** * Manage sharing events * @param array $params The hook params */ public static function share($params) { self::getHooks()->share($params); } /** * Unsharing event * @param GenericEvent $event */ public static function unShare(GenericEvent $event) { $share = $event->getSubject(); if ($share instanceof IShare) { self::getHooks()->unShare($share); } } } Extension/LegacyParser.php 0000604 00000004503 15247130324 0011611 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Activity\Extension; use OCA\Activity\DataHelper; use OCA\Activity\PlainTextParser; use OCP\Activity\IEvent; use OCP\Activity\IProvider; use OCP\L10N\IFactory; class LegacyParser implements IProvider { /** @var IFactory */ protected $languageFactory; /** @var DataHelper */ protected $dataHelper; /** @var PlainTextParser */ protected $parser; /** * @param IFactory $languageFactory * @param DataHelper $dataHelper * @param PlainTextParser $parser */ public function __construct(IFactory $languageFactory, DataHelper $dataHelper, PlainTextParser $parser) { $this->languageFactory = $languageFactory; $this->dataHelper = $dataHelper; $this->parser = $parser; } /** * @param string $language * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @throws \InvalidArgumentException * @since 9.2.0 */ public function parse($language, IEvent $event, IEvent $previousEvent = null) { $l = $this->languageFactory->get('activity', $language); $this->dataHelper->setL10n($l); $event->setParsedSubject($this->parser->parseMessage($this->dataHelper->translation( $event->getApp(), $event->getSubject(), $this->dataHelper->getParameters($event, 'subject', json_encode($event->getSubjectParameters())) ))); $event->setParsedMessage($this->parser->parseMessage($this->dataHelper->translation( $event->getApp(), $event->getMessage(), $this->dataHelper->getParameters($event, 'message', json_encode($event->getMessageParameters())) ))); return $event; } } Extension/Files.php 0000604 00000001731 15247130324 0010272 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Extension; class Files { const TYPE_SHARE_CREATED = 'file_created'; const TYPE_SHARE_CHANGED = 'file_changed'; const TYPE_SHARE_DELETED = 'file_deleted'; const TYPE_SHARE_RESTORED = 'file_restored'; } Extension/Files_Sharing.php 0000604 00000001516 15247130324 0011746 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity\Extension; class Files_Sharing { const TYPE_SHARED = 'shared'; } Data.php 0000604 00000031415 15247130324 0006127 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Frank Karlitschek <frank@karlitschek.de> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Activity; use OCP\Activity\IEvent; use OCP\Activity\IExtension; use OCP\Activity\IFilter; use OCP\Activity\IManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IL10N; /** * @brief Class for managing the data in the activities */ class Data { /** @var IManager */ protected $activityManager; /** @var IDBConnection */ protected $connection; /** * @param IManager $activityManager * @param IDBConnection $connection */ public function __construct(IManager $activityManager, IDBConnection $connection) { $this->activityManager = $activityManager; $this->connection = $connection; } /** * Send an event into the activity stream * * @param IEvent $event * @return bool */ public function send(IEvent $event) { if ($event->getAffectedUser() === '' || $event->getAffectedUser() === null) { return false; } // store in DB $queryBuilder = $this->connection->getQueryBuilder(); $queryBuilder->insert('activity') ->values([ 'app' => $queryBuilder->createParameter('app'), 'subject' => $queryBuilder->createParameter('subject'), 'subjectparams' => $queryBuilder->createParameter('subjectparams'), 'message' => $queryBuilder->createParameter('message'), 'messageparams' => $queryBuilder->createParameter('messageparams'), 'file' => $queryBuilder->createParameter('object_name'), 'link' => $queryBuilder->createParameter('link'), 'user' => $queryBuilder->createParameter('user'), 'affecteduser' => $queryBuilder->createParameter('affecteduser'), 'timestamp' => $queryBuilder->createParameter('timestamp'), 'priority' => $queryBuilder->createParameter('priority'), 'type' => $queryBuilder->createParameter('type'), 'object_type' => $queryBuilder->createParameter('object_type'), 'object_id' => $queryBuilder->createParameter('object_id'), ]) ->setParameters([ 'app' => $event->getApp(), 'type' => $event->getType(), 'affecteduser' => $event->getAffectedUser(), 'user' => $event->getAuthor(), 'timestamp' => (int) $event->getTimestamp(), 'subject' => $event->getSubject(), 'subjectparams' => json_encode($event->getSubjectParameters()), 'message' => $event->getMessage(), 'messageparams' => json_encode($event->getMessageParameters()), 'priority' => IExtension::PRIORITY_MEDIUM, 'object_type' => $event->getObjectType(), 'object_id' => (int) $event->getObjectId(), 'object_name' => $event->getObjectName(), 'link' => $event->getLink(), ]) ->execute(); return true; } /** * Send an event as email * * @param IEvent $event * @param int $latestSendTime Activity $timestamp + batch setting of $affectedUser * @return bool */ public function storeMail(IEvent $event, $latestSendTime) { if ($event->getAffectedUser() === '' || $event->getAffectedUser() === null) { return false; } // store in DB $queryBuilder = $this->connection->getQueryBuilder(); $queryBuilder->insert('activity_mq') ->values([ 'amq_appid' => $queryBuilder->createParameter('app'), 'amq_subject' => $queryBuilder->createParameter('subject'), 'amq_subjectparams' => $queryBuilder->createParameter('subjectparams'), 'amq_affecteduser' => $queryBuilder->createParameter('affecteduser'), 'amq_timestamp' => $queryBuilder->createParameter('timestamp'), 'amq_type' => $queryBuilder->createParameter('type'), 'amq_latest_send' => $queryBuilder->createParameter('latest_send'), ]) ->setParameters([ 'app' => $event->getApp(), 'subject' => $event->getSubject(), 'subjectparams' => json_encode($event->getSubjectParameters()), 'affecteduser' => $event->getAffectedUser(), 'timestamp' => (int) $event->getTimestamp(), 'type' => $event->getType(), 'latest_send' => $latestSendTime, ]) ->execute(); return true; } /** * Read a list of events from the activity stream * * @param GroupHelper $groupHelper Allows activities to be grouped * @param UserSettings $userSettings Gets the settings of the user * @param string $user User for whom we display the stream * * @param int $since The integer ID of the last activity that has been seen. * @param int $limit How many activities should be returned * @param string $sort Should activities be given ascending or descending * * @param string $filter Filter the activities * @param string $objectType Allows to filter the activities to a given object. May only appear together with $objectId * @param int $objectId Allows to filter the activities to a given object. May only appear together with $objectType * * @return array * * @throws \OutOfBoundsException if the user (Code: 1) or the since (Code: 2) is invalid * @throws \BadMethodCallException if the user has selected to display no types for this filter (Code: 3) */ public function get(GroupHelper $groupHelper, UserSettings $userSettings, $user, $since, $limit, $sort, $filter, $objectType = '', $objectId = 0) { // get current user if ($user === '') { throw new \OutOfBoundsException('Invalid user', 1); } $groupHelper->setUser($user); $activeFilter = null; try { $activeFilter = $this->activityManager->getFilterById($filter); } catch (\InvalidArgumentException $e) { // Unknown filter => ignore and show all activities } $enabledNotifications = $userSettings->getNotificationTypes($user, 'stream'); if ($activeFilter instanceof IFilter) { $enabledNotifications = $activeFilter->filterTypes($enabledNotifications); } $enabledNotifications = array_unique($enabledNotifications); // We don't want to display any activities if (empty($enabledNotifications)) { throw new \BadMethodCallException('No settings enabled', 3); } $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('activity'); $query->where($query->expr()->eq('affecteduser', $query->createNamedParameter($user))) ->andWhere($query->expr()->in('type', $query->createNamedParameter($enabledNotifications, IQueryBuilder::PARAM_STR_ARRAY))); if ($filter === 'self') { $query->andWhere($query->expr()->eq('user', $query->createNamedParameter($user))); } else if ($filter === 'by') { $query->andWhere($query->expr()->neq('user', $query->createNamedParameter($user))); } else if ($filter === 'all' && !$userSettings->getUserSetting($user, 'setting', 'self')) { $query->andWhere($query->expr()->orX( $query->expr()->neq('user', $query->createNamedParameter($user)), $query->expr()->notIn('type', $query->createNamedParameter([ 'file_created', 'file_changed', 'file_deleted', 'file_restored', ], IQueryBuilder::PARAM_STR_ARRAY)) )); } else if ($filter === 'filter') { if (!$userSettings->getUserSetting($user, 'setting', 'self')) { $query->andWhere($query->expr()->orX( $query->expr()->neq('user', $query->createNamedParameter($user)), $query->expr()->notIn('type', $query->createNamedParameter([ 'file_created', 'file_changed', 'file_deleted', 'file_restored', ], IQueryBuilder::PARAM_STR_ARRAY)) )); } $query->andWhere($query->expr()->eq('object_type', $query->createNamedParameter($objectType))); $query->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId))); } if ($activeFilter instanceof IFilter) { $apps = $activeFilter->allowedApps(); if (!empty($apps)) { $query->andWhere($query->expr()->in('app', $query->createNamedParameter($apps, IQueryBuilder::PARAM_STR_ARRAY))); } } if ( $filter === 'files_favorites' || (in_array($filter, ['all', 'by', 'self']) && $userSettings->getUserSetting($user, 'stream', 'files_favorites')) ) { try { $favoriteFilter = $this->activityManager->getFilterById('files_favorites'); /** @var \OCA\Files\Activity\Filter\Favorites $favoriteFilter */ $favoriteFilter->filterFavorites($query); } catch (\InvalidArgumentException $e) { } } /** * Order and specify the offset */ $sqlSort = ($sort === 'asc') ? 'ASC' : 'DESC'; $headers = $this->setOffsetFromSince($query, $user, $since, $sqlSort); $query->orderBy('timestamp', $sqlSort) ->addOrderBy('activity_id', $sqlSort); $query->setMaxResults($limit + 1); $result = $query->execute(); $hasMore = false; while ($row = $result->fetch()) { if ($limit === 0) { $hasMore = true; break; } $headers['X-Activity-Last-Given'] = (int) $row['activity_id']; $groupHelper->addActivity($row); $limit--; } $result->closeCursor(); return ['data' => $groupHelper->getActivities(), 'has_more' => $hasMore, 'headers' => $headers]; } /** * @param IQueryBuilder $query * @param string $user * @param int $since * @param string $sort * * @return array Headers that should be set on the response * * @throws \OutOfBoundsException If $since is not owned by $user */ protected function setOffsetFromSince(IQueryBuilder $query, $user, $since, $sort) { if ($since) { $queryBuilder = $this->connection->getQueryBuilder(); $queryBuilder->select(['affecteduser', 'timestamp']) ->from('activity') ->where($queryBuilder->expr()->eq('activity_id', $queryBuilder->createNamedParameter((int) $since))); $result = $queryBuilder->execute(); $activity = $result->fetch(); $result->closeCursor(); if ($activity) { if ($activity['affecteduser'] !== $user) { throw new \OutOfBoundsException('Invalid since', 2); } $timestamp = (int) $activity['timestamp']; if ($sort === 'DESC') { $query->andWhere($query->expr()->lte('timestamp', $query->createNamedParameter($timestamp))); $query->andWhere($query->expr()->lt('activity_id', $query->createNamedParameter($since))); } else { $query->andWhere($query->expr()->gte('timestamp', $query->createNamedParameter($timestamp))); $query->andWhere($query->expr()->gt('activity_id', $query->createNamedParameter($since))); } return []; } } /** * Couldn't find the since, so find the oldest one and set the header */ $fetchQuery = $this->connection->getQueryBuilder(); $fetchQuery->select('activity_id') ->from('activity') ->where($fetchQuery->expr()->eq('affecteduser', $fetchQuery->createNamedParameter($user))) ->orderBy('timestamp', $sort) ->setMaxResults(1); $result = $fetchQuery->execute(); $activity = $result->fetch(); $result->closeCursor(); if ($activity !== false) { return [ 'X-Activity-First-Known' => (int) $activity['activity_id'], ]; } return []; } /** * Verify that the filter is valid * * @param string $filterValue * @return string */ public function validateFilter($filterValue) { if (!isset($filterValue)) { return 'all'; } switch ($filterValue) { case 'filter': return $filterValue; default: try { $this->activityManager->getFilterById($filterValue); return $filterValue; } catch (\InvalidArgumentException $e) { return 'all'; } } } /** * Delete old events * * @param int $expireDays Minimum 1 day */ public function expire($expireDays = 365) { $ttl = (60 * 60 * 24 * max(1, $expireDays)); $timelimit = time() - $ttl; $this->deleteActivities(array( 'timestamp' => array($timelimit, '<'), )); } /** * Delete activities that match certain conditions * * @param array $conditions Array with conditions that have to be met * 'field' => 'value' => `field` = 'value' * 'field' => array('value', 'operator') => `field` operator 'value' */ public function deleteActivities($conditions) { $sqlWhere = ''; $sqlParameters = $sqlWhereList = array(); foreach ($conditions as $column => $comparison) { $sqlWhereList[] = " `$column` " . ((is_array($comparison) && isset($comparison[1])) ? $comparison[1] : '=') . ' ? '; $sqlParameters[] = (is_array($comparison)) ? $comparison[0] : $comparison; } if (!empty($sqlWhereList)) { $sqlWhere = ' WHERE ' . implode(' AND ', $sqlWhereList); } $query = $this->connection->prepare( 'DELETE FROM `*PREFIX*activity`' . $sqlWhere); $query->execute($sqlParameters); } } l10n/he.json 0000604 00000046352 15247130447 0006622 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "לא ניתן לכתוב לתיקיית \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "בדרך כלל ניתן לפתור את הבעיה על ידי כך שנותנים ל- webserver הרשאות כניסה לתיקיית confg", "See %s" : "ניתן לראות %s", "Sample configuration detected" : "התגלתה דוגמת תצורה", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "התגלה שדוגמת התצורה הועתקה. דבר זה עלול לשבור את ההתקנה ולא נתמך.יש לקרוא את מסמכי התיעוד לפני שמבצעים שינויים ב- config.php", "PHP %s or higher is required." : "נדרש PHP בגרסת %s ומעלה.", "PHP with a version lower than %s is required." : "נדרש PHP בגרסה נמוכה מ- %s.", "%sbit or higher PHP required." : "נדרש PHP בגרסת %s ומעלה.", "Following databases are supported: %s" : "מסדי הנתונים הבאים נתמכים: %s", "The command line tool %s could not be found" : "כלי שורת הפקודה %s לא אותר", "The library %s is not available." : "הספריה %s אינה זמינה.", "Library %s with a version higher than %s is required - available version %s." : "ספריה %s בגרסה גבוהה מ- %s נדרשת - גרסה זמינה %s.", "Library %s with a version lower than %s is required - available version %s." : "ספריה %s בגרסה נמוכה מ- %s נדרשת - גרסה זמינה %s.", "Following platforms are supported: %s" : "הפלטפורמות הבאות נתמכות: %s", "Unknown filetype" : "סוג קובץ לא מוכר", "Invalid image" : "תמונה לא חוקית", "today" : "היום", "yesterday" : "אתמול", "_%n day ago_::_%n days ago_" : ["לפני %n יום","לפני %n ימים"], "last month" : "חודש שעבר", "last year" : "שנה שעברה", "_%n year ago_::_%n years ago_" : ["לפני %n שנה","לפני %n שנים"], "seconds ago" : "שניות", "File name is a reserved word" : "שם קובץ הנו מילה שמורה", "File name contains at least one invalid character" : "שם קובץ כולל לפחות תו אחד לא חוקי", "File name is too long" : "שם קובץ ארוך מדי", "Dot files are not allowed" : "קבצי Dot אינם מותרים", "Empty filename is not allowed" : "שם קובץ ריק אינו מאושר", "App \"%s\" cannot be installed because appinfo file cannot be read." : "יישום \"%s\" לא ניתן להתקנה כיוון שקובץ appinfo לא ניתן לקריאה.", "Help" : "עזרה", "Apps" : "יישומים", "Settings" : "הגדרות", "Log out" : "התנתק", "Users" : "משתמשים", "Sharing" : "שיתוף", "Tips & tricks" : "טיפים וטריקים", "%s enter the database username and name." : "%s יש להכניס את שם המשתמש ושם מסד הנתונים.", "%s enter the database username." : "%s נכנס למסד נתוני שמות המשתמשים.", "%s enter the database name." : "%s נכנס למסד נתוני השמות.", "%s you may not use dots in the database name" : "%s לא ניתן להשתמש בנקודות בשם מסד הנתונים", "Oracle connection could not be established" : "לא ניתן היה ליצור חיבור Oracle", "Oracle username and/or password not valid" : "שם משתמש ו/או סיסמת Oracle אינם תקפים", "PostgreSQL username and/or password not valid" : "שם משתמש ו/או סיסמת PostgreSQL אינם תקפים", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X אינו נתמך ו- %s לא יעבוד כשורה בפלטפורמה זו. ניתן לקחת סיכון ולהשתמש באחריותך! ", "For the best results, please consider using a GNU/Linux server instead." : "לתוצאות הכי טובות, יש לשקול שימוש בשרת GNU/Linux במקום.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "נראה ש- %s עובד על בסיס סביבת 32-bit PHP ושה- open_basedir הוגדר בקובץ php.ini. מצב זה יוביל לבעיות עם קבצים הגדולים מ- 4 GB ואינו מומלץ לחלוטין.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "יש להסיר את הגדרת open_basedir מתוך קובץ php.ini או להחליף לסביבת 64-bit PHP.", "Set an admin username." : "קביעת שם משתמש מנהל", "Set an admin password." : "קביעת סיסמת מנהל", "Can't create or write into the data directory %s" : "לא ניתן ליצור או לכתוב לתוך תיקיית הנתונים %s", "Invalid Federated Cloud ID" : "זיהוי ענן מאוגד לא חוקי", "Sharing %s failed, because the backend does not allow shares from type %i" : "השיתוף %s נכשל, כיוון שהצד האחורי אינו מאפשר שיתופים מסוג %i", "Sharing %s failed, because the file does not exist" : "השיתוף %s נכשל, כיוון שהקובץ אינו קיים", "You are not allowed to share %s" : "אינך רשאי/ת לשתף %s", "Sharing %s failed, because you can not share with yourself" : "השיתוף %s נכשל, כיוון שלא ניתן לשתף עם עצמך", "Sharing %s failed, because the user %s does not exist" : "השיתוף %s נכשל, כיוון שהמשתמש %s אינו קיים", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "השיתוף %s נכשל, כיוון שהמשתמש %s אינו חבר בקבוצות ש- %s חבר ב-", "Sharing %s failed, because this item is already shared with %s" : "שיתוף %s נכשל, כיוון שפריט זה כבר משותף עם %s", "Sharing %s failed, because this item is already shared with user %s" : "השיתוף %s נכשל, כיוון שהפריט כבר משותף עם משתמש %s", "Sharing %s failed, because the group %s does not exist" : "השיתוף %s נכשל, כיוון שהקבוצה %s אינה קיימת", "Sharing %s failed, because %s is not a member of the group %s" : "השיתוף %s נכשל, כיוון ש- %s אינו חבר בקבוצה %s", "You need to provide a password to create a public link, only protected links are allowed" : "יש לספק סיסמא ליצירת קישור ציבורי, רק קישורים מוגנים מותרים", "Sharing %s failed, because sharing with links is not allowed" : "השיתוף %s נכשל, כיוון ששיתוף עם קישור אינו מותר", "Not allowed to create a federated share with the same user" : "אסור ליצור שיתוף מאוגד עם אותו משתמש", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "שיתוף %s נכשל, לא ניתן לאתר %s, ייתכן שהשרת לא ניתן להשגה כרגע.", "Share type %s is not valid for %s" : "שיתוף מסוג %s אינו תקף ל- %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "לא ניתן לקבוע תאריך תפוגה. שיתופים אינם יכולים לפוג תוקף מאוחר יותר מ- %s לאחר ששותפו", "Cannot set expiration date. Expiration date is in the past" : "לא ניתן לקבוע תאריך תפוגה. תאריך התפוגה הנו בעבר", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "צד אחורי לשיתוף %s חייב ליישם את ממשק OCP\\Share_Backend", "Sharing backend %s not found" : "צד אחורי לשיתוף %s לא נמצא", "Sharing backend for %s not found" : "צד אחורי לשיתוף של %s לא נמצא", "Sharing failed, because the user %s is the original sharer" : "שיתוף נכשל, כיוון שמשתמש %s הנו המשתף המקורי", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "השיתוף %s נכשל, כיוון שההרשאות עלו על ההרשאות שניתנו ל- %s", "Sharing %s failed, because resharing is not allowed" : "השיתוף %s נכשל, כיוון ששיתוף מחודש אסור", "Sharing %s failed, because the sharing backend for %s could not find its source" : "השיתוף %s נכשל, כיוון שבצד אחורי לשיתוף עבור %s לא ניתן היה לאתר את מקורו", "Sharing %s failed, because the file could not be found in the file cache" : "השיתוף %s נכשל, כייון שלא ניתן היה למצוא את הקובץ בזכרון המטמון", "Expiration date is in the past" : "תאריך תפוגה הנו בעבר", "%s shared »%s« with you" : "%s שיתף/שיתפה איתך את »%s«", "%s via %s" : "%s על בסיס %s", "Could not find category \"%s\"" : "לא ניתן למצוא את הקטגוריה „%s“", "Sunday" : "יום ראשון", "Monday" : "יום שני", "Tuesday" : "יום שלישי", "Wednesday" : "יום רביעי", "Thursday" : "יום חמישי", "Friday" : "יום שישי", "Saturday" : "שבת", "Sun." : "ראשון", "Mon." : "שני", "Tue." : "שלישי", "Wed." : "רביעי", "Thu." : "חמישי", "Fri." : "שישי", "Sat." : "שבת", "Su" : "א", "Mo" : "ב", "Tu" : "ג", "We" : "ד", "Th" : "ה", "Fr" : "ו", "Sa" : "ש", "January" : "ינואר", "February" : "פברואר", "March" : "מרץ", "April" : "אפריל", "May" : "מאי", "June" : "יוני", "July" : "יולי", "August" : "אוגוסט", "September" : "ספטמבר", "October" : "אוקטובר", "November" : "נובמבר", "December" : "דצמבר", "Jan." : "ינו׳", "Feb." : "פבר׳", "Mar." : "מרץ", "Apr." : "אפר׳", "May." : "מאי", "Jun." : "יונ׳", "Jul." : "יול׳", "Aug." : "אוג׳", "Sep." : "ספט׳", "Oct." : "אוק׳", "Nov." : "נוב׳", "Dec." : "דצמ׳", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "רק התווים הבאים מאושרים לשם משתמש: \"a-z\", \"A-Z\", \"0-9\", וגם \"_.@-'\"", "A valid username must be provided" : "יש לספק שם משתמש תקני", "Username contains whitespace at the beginning or at the end" : "שם המשתמש מכיל רווח בתחילתו או בסופו", "A valid password must be provided" : "יש לספק ססמה תקנית", "The username is already being used" : "השם משתמש כבר בשימוש", "User disabled" : "משתמש מנוטרל", "Login canceled by app" : "התחברות בוטלה על ידי יישום", "No app name specified" : "לא הוגדר שם יישום", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "היישום \"%s\" לא ניתן להתקנה כיוון שיחסי התלות הבאים אינם מתקיימים: %s", "a safe home for all your data" : "בית בטוח עבור כל המידע שלך", "File is currently busy, please try again later" : "הקובץ בשימוש כרגע, יש לנסות שוב מאוחר יותר", "Can't read file" : "לא ניתן לקרוא קובץ", "Application is not enabled" : "יישומים אינם מופעלים", "Authentication error" : "שגיאת הזדהות", "Token expired. Please reload page." : "פג תוקף. נא לטעון שוב את הדף.", "Unknown user" : "משתמש לא ידוע", "No database drivers (sqlite, mysql, or postgresql) installed." : "לא מותקנים דרייברים למסד הנתונים (sqlite, mysql, או postgresql).", "Cannot write into \"config\" directory" : "לא ניתן לכתוב לתיקיית \"config\"!", "Cannot write into \"apps\" directory" : "לא ניתן לכתוב לתיקיית \"apps\"", "Setting locale to %s failed" : "הגדרת שפה ל- %s נכשלה", "Please install one of these locales on your system and restart your webserver." : "יש להתקין אחת מהשפות על המערכת שלך ולהפעיל מחדש את שרת האינטרנט.", "Please ask your server administrator to install the module." : "יש לבקש ממנהל השרת שלך להתקין את המודול.", "PHP module %s not installed." : "מודול PHP %s אינו מותקן.", "PHP setting \"%s\" is not set to \"%s\"." : "הגדרות PHP \"%s\" אינם מוגדרות ל- \"%s\"", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload מוגדר ל- \"%s\" במקום הערך המצופה \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "לתיקון בעיה זו יש להגדיר <code>mbstring.func_overload</code> כ- <code>0</code> iבקובץ ה- php.ini שלך", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 נדרש לכל הפחות. כרגע %s מותקן.", "To fix this issue update your libxml2 version and restart your web server." : "לתיקון הבעיה יש לעדכן את גרסת ה- libxml2 שלך ולהפעיל מחדש את שרת האינטרנט שלך.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ככל הנראה מוגדר ל- strip inline doc blocks. זה יגרום למספר יישומי ליבה לא להיות נגישים.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "זה ככל הנראה נגרם על ידי מאיץ/מטמון כמו Zend OPcache או eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "מודולי PHP הותקנו, אך עדיין רשומים כחסרים?", "Please ask your server administrator to restart the web server." : "יש לבקש ממנהל השרת שלך להפעיל מחדש את שרת האינטרנט.", "PostgreSQL >= 9 required" : "נדרש PostgreSQL >= 9", "Please upgrade your database version" : "יש לשדרג את גרסת מסד הנתונים שלך", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "יש לשנות את ההרשאות ל- 0770 כך שהתיקייה לא תרשם על ידי משתמשים אחרים.", "Check the value of \"datadirectory\" in your configuration" : "יש לבדוק את הערך \"datadirectory\" בהגדרות התצורה שלך", "Could not obtain lock type %d on \"%s\"." : "לא ניתן היה להשיג סוג נעילה %d ב- \"%s\".", "Storage unauthorized. %s" : "אחסון לא מורשה. %s", "Storage incomplete configuration. %s" : "תצורה לא מושלמת של האחסון. %s", "Storage connection error. %s" : "שגיאת חיבור אחסון. %s", "Storage connection timeout. %s" : "פסק זמן חיבור אחסון. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "בדרך כלל ניתן לפתור את הבעיה על ידי כך ש- %s נותן ל- webserver הרשאות כניסה לתיקיית config %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "מודול עם זהות: %s אינו קיים. יש לאפשר את זה בהגדרות היישומים או ליצור קשר עם המנהל.", "Server settings" : "הגדרות שרת", "DB Error: \"%s\"" : "שגיאת מסד נתונים: \"%s\"", "Offending command was: \"%s\"" : "הפקודה המזיקה הייתה: \"%s\"", "You need to enter either an existing account or the administrator." : "יש להכניס חשבון קיים או מנהל.", "Offending command was: \"%s\", name: %s, password: %s" : "הפקודה המזיקה הייתה: \"%s\", שם: %s, סיסמא: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "הגדרת הרשאות ל- %s נכשלה, כיוון שההרשאות עולים על האישורים שניתנו ל- %s", "Setting permissions for %s failed, because the item was not found" : "הגדרת הרשאות ל- %s נכשלה, כיוון שהפריט לא נמצא", "Cannot clear expiration date. Shares are required to have an expiration date." : "לא ניתן לבטל תאריך תפוגה. שיתופים חייבים להכיל תאריך תפוגה.", "Cannot increase permissions of %s" : "לא ניתן להגדיל את ההיתרים של %s", "Files can't be shared with delete permissions" : "קובץ לא ניתן לשיתוף בפעולת מחיקת הרשאות", "Files can't be shared with create permissions" : "קובץ לא ניתן לשיתוף בפעולת יצירת הרשאות", "Cannot set expiration date more than %s days in the future" : "לא ניתן להגדיר את תאריך התפוגה מעל %s ימים בעתיד", "Personal" : "אישי", "Admin" : "מנהל", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "בדרך כלל ניתן להסתדר על ידי %s מתן הרשאות כתיבה בשרת האינטרנט לתיקיית היישומים %s או נטרול חנות היישומים בקובץ ה- config.", "Cannot create \"data\" directory (%s)" : "לא ניתן ליצור תיקיית \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "זה בדרך כלל ניתן לתיקון על ידי <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">מתן הרשאות כתיבה בשרת לתיקיית הבסיס directory</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "הרשאות ניתנות בדרך כלל לתיקון על ידי %s מתן לשרת האינטרנט גישת כתיבה לתיקיית הבסיס %s.", "Data directory (%s) is readable by other users" : "תיקיית המידע (%s) ניתנת לקריאה על ידי משתמשים אחרים", "Data directory (%s) must be an absolute path" : "תיקיית המידע (%s) חייבת להיות כנתיב אבסולוטי", "Data directory (%s) is invalid" : "תיקיית מידע (%s) אינה חוקית", "Please check that the data directory contains a file \".ocdata\" in its root." : "יש לוודא שתיקיית המידע כוללת קובץ \".ocdata\" בנתיב הבסיס שלה" },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/ru.json 0000604 00000073576 15247130447 0006664 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Запись в каталог «config» невозможна!", "This can usually be fixed by giving the webserver write access to the config directory" : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог конфигурации", "See %s" : "Смотрите %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог конфигурации. Смотрите %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Файлы приложения %$1s не заменены корректно. Проверьте что его версия совместима с версией сервера.", "Sample configuration detected" : "Обнаружена конфигурация из примера", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Была обнаружена конфигурация из примера. Такая конфигурация не поддерживается и может повредить вашей системе. Прочтите документацию перед внесением изменений в файл config.php", "%1$s and %2$s" : "%1$s и %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s и %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s и %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s и %5$s", "Education Edition" : "Образовательная редакция", "Enterprise bundle" : "Корпоративный пакет", "Groupware bundle" : "Пакет для групп", "Social sharing bundle" : "Пакет для соц. сетей", "PHP %s or higher is required." : "Требуется PHP %s или выше", "PHP with a version lower than %s is required." : "Требуется версия PHP ниже %s.", "%sbit or higher PHP required." : "Требуется PHP с разрядностью %s бит или более.", "Following databases are supported: %s" : "Поддерживаются следующие СУБД: %s", "The command line tool %s could not be found" : "Утилита командной строки %s не найдена", "The library %s is not available." : "Библиотека %s недоступна.", "Library %s with a version higher than %s is required - available version %s." : "Требуется библиотека %s версии не меньше %s, установлена версия %s.", "Library %s with a version lower than %s is required - available version %s." : "Требуется библиотека %s версии не выше %s, установлена версия %s.", "Following platforms are supported: %s" : "Поддерживаются следующие платформы: %s", "Server version %s or higher is required." : "Требуется сервер версии %s или выше.", "Server version %s or lower is required." : "Требуется сервер версии %s или ниже.", "Unknown filetype" : "Неизвестный тип файла", "Invalid image" : "Изображение повреждено", "Avatar image is not square" : "Изображение аватара не квадратное", "today" : "сегодня", "yesterday" : "вчера", "_%n day ago_::_%n days ago_" : ["%n день назад","%n дня назад","%n дней назад","%n дней назад"], "last month" : "в прошлом месяце", "_%n month ago_::_%n months ago_" : ["%n месяц назад","%n месяца назад","%n месяцев назад","%n месяцев назад"], "last year" : "в прошлом году", "_%n year ago_::_%n years ago_" : ["%n год назад","%n года назад","%n лет назад","%n лет назад"], "_%n hour ago_::_%n hours ago_" : ["%n час назад","%n часа назад","%n часов назад","%n часов назад"], "_%n minute ago_::_%n minutes ago_" : ["%n минута назад","%n минуты назад","%n минут назад","%n минут назад"], "seconds ago" : "менее минуты", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модуль с ID «%s» не существует. Включите его в настройках приложений или обратитесь к администратору.", "File name is a reserved word" : "Имя файла является зарезервированным словом", "File name contains at least one invalid character" : "Имя файла содержит по крайней мере один некорректный символ", "File name is too long" : "Имя файла слишком длинное.", "Dot files are not allowed" : "Файлы начинающиеся с точки не допускаются", "Empty filename is not allowed" : "Пустое имя файла не допускается", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Приложение «%s» не может быть установлено, так как файл с информацией о приложении не может быть прочтен.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Приложение «%s» не может быть установлено, потому что оно несовместимо с этой версией сервера", "This is an automatically sent email, please do not reply." : "Это соощение отправлено автоматически, пожалуйста, не отвечайте на него.", "Help" : "Помощь", "Apps" : "Приложения", "Settings" : "Настройки", "Log out" : "Выйти", "Users" : "Пользователи", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Основные настройки", "Sharing" : "Общий доступ", "Security" : "Безопасность", "Encryption" : "Шифрование", "Additional settings" : "Дополнительные настройки", "Tips & tricks" : "Советы и трюки", "Personal info" : "Личная информация", "Sync clients" : "Клиенты синхронизации", "Unlimited" : "Неограничено", "__language_name__" : "Русский", "Verifying" : "Производится проверка", "Verifying …" : "Производится проверка…", "Verify" : "Проверить", "%s enter the database username and name." : "%s укажите имя пользователя и название для базы данных.", "%s enter the database username." : "%s введите имя пользователя базы данных.", "%s enter the database name." : "%s введите имя базы данных.", "%s you may not use dots in the database name" : "%s Вы не можете использовать точки в имени базы данных", "Oracle connection could not be established" : "Соединение с Oracle не может быть установлено", "Oracle username and/or password not valid" : "Неверное имя пользователя и/или пароль Oracle", "PostgreSQL username and/or password not valid" : "Неверное имя пользователя и/или пароль PostgreSQL", "You need to enter details of an existing account." : "Необходимо уточнить данные существующего акаунта.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X не поддерживается и %s может работать некорректно на данной платформе. Используйте на свой страх и риск!", "For the best results, please consider using a GNU/Linux server instead." : "Для достижения наилучших результатов, рассмотрите вариант использования сервера на GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Кажется что экземпляр этого %s работает в 32-битной среде PHP и в php.ini был настроен open_basedir. Это приведёт к проблемам с файлами более 4 ГБ и настоятельно не рекомендуется.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Пожалуйста, удалите директиву open_basedir из файла php.ini или смените PHP на 64-разрядную сборку.", "Set an admin username." : "Задать имя пользователя для администратора.", "Set an admin password." : "Задать пароль для admin.", "Can't create or write into the data directory %s" : "Невозможно создать или записать в каталог данных %s", "Invalid Federated Cloud ID" : "Неверный ID в объединении облачных хранилищ.", "Sharing %s failed, because the backend does not allow shares from type %i" : "Не удалось поделиться %s, так как механизм хранения не допускает публикации из элементов типа %i", "Sharing %s failed, because the file does not exist" : "Не удалось поделиться %s, файл не существует", "You are not allowed to share %s" : "Вам не разрешено делиться %s", "Sharing %s failed, because you can not share with yourself" : "Не удалось поделиться %s. Вы не можете поделиться с самим собой.", "Sharing %s failed, because the user %s does not exist" : "Не удалось поделиться %s, пользователь %s не существует.", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Не удалось поделиться %s, так как пользователь %s не состоит в какой-либо группе, в которой состоит %s", "Sharing %s failed, because this item is already shared with %s" : "Не удалось поделиться %s, пользователь %s уже имеет доступ к этому элементу", "Sharing %s failed, because this item is already shared with user %s" : "Не удалось поделиться %s, так как элемент находится в общем доступе у %s", "Sharing %s failed, because the group %s does not exist" : "Не удалось поделиться %s, группа %s не существует", "Sharing %s failed, because %s is not a member of the group %s" : "Не удалось поделиться %s, пользователь %s не является членом группы %s", "You need to provide a password to create a public link, only protected links are allowed" : "Вам нужно задать пароль для создания публичной ссылки. Разрешены только защищённые ссылки", "Sharing %s failed, because sharing with links is not allowed" : "Не удалось поделиться %s, открытие доступа по ссылке запрещено", "Not allowed to create a federated share with the same user" : "Не допускается создание федеративного общего ресурса с тем же пользователем", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Не удалось поделиться %s, не удалось найти %s, возможно, сервер не доступен.", "Share type %s is not valid for %s" : "Тип общего доступа %s недопустим для %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Невозможно установить дату истечения. Общие ресурсы не могут устареть позже %s с момента их публикации.", "Cannot set expiration date. Expiration date is in the past" : "Невозможно установить дату окончания. Дата окончания в прошлом.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Бэкенд общего доступа %s должен реализовывать интерфейс OCP\\Share_Backend", "Sharing backend %s not found" : "Бэкенд общего доступа %s не найден", "Sharing backend for %s not found" : "Бэкенд общего доступа для %s не найден", "Sharing failed, because the user %s is the original sharer" : "Не удалось поделиться, потому что пользователь %s владелец этого элемента", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Не удалось поделиться %s, права превышают предоставленные права доступа %s", "Sharing %s failed, because resharing is not allowed" : "Не удалось поделиться %s, повторное открытие доступа запрещено", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Не удалось поделиться %s, бэкенд общего доступа не нашел путь до %s", "Sharing %s failed, because the file could not be found in the file cache" : "Не удалось поделиться %s, элемент не найден в файловом кеше.", "Can’t increase permissions of %s" : "Невозможно увеличить права доступа для %s", "Files can’t be shared with delete permissions" : "Файлы не могут иметь общий доступ с правами на удаление", "Files can’t be shared with create permissions" : "Файлы не могут иметь общий доступ с правами на создание", "Expiration date is in the past" : "Дата окончания срока действия уже прошла", "Can’t set expiration date more than %s days in the future" : "Невозможно установить дату окончания срока действия более %s дней", "%s shared »%s« with you" : "%s поделился »%s« с вами", "%s shared »%s« with you." : "%s поделился »%s« с вами.", "Click the button below to open it." : "Для открытия нажмите на кнопку ниже.", "Open »%s«" : "Открыть »%s«", "%s via %s" : "%s через %s", "The requested share does not exist anymore" : "Запрошенный общий ресурс более не существует.", "Could not find category \"%s\"" : "Категория «%s» не найдена", "Sunday" : "Воскресенье", "Monday" : "Понедельник", "Tuesday" : "Вторник", "Wednesday" : "Среда", "Thursday" : "Четверг", "Friday" : "Пятница", "Saturday" : "Суббота", "Sun." : "Вс.", "Mon." : "Пн.", "Tue." : "Вт.", "Wed." : "Ср.", "Thu." : "Чт.", "Fri." : "Пт.", "Sat." : "Сб.", "Su" : "Вс", "Mo" : "Пн", "Tu" : "Вт", "We" : "Ср", "Th" : "Чт", "Fr" : "Пт", "Sa" : "Сб", "January" : "Январь", "February" : "Февраль", "March" : "Март", "April" : "Апрель", "May" : "Май", "June" : "Июнь", "July" : "Июль", "August" : "Август", "September" : "Сентябрь", "October" : "Октябрь", "November" : "Ноябрь", "December" : "Декабрь", "Jan." : "Янв.", "Feb." : "Фев.", "Mar." : "Мар.", "Apr." : "Апр.", "May." : "Май", "Jun." : "Июн.", "Jul." : "Июл.", "Aug." : "Авг.", "Sep." : "Сен.", "Oct." : "Окт.", "Nov." : "Нояб.", "Dec." : "Дек.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "В составе имени пользователя допускаются следующие символы: «a–z», «A–Z», «0–9» и «_.@-'»", "A valid username must be provided" : "Укажите допустимое имя пользователя", "Username contains whitespace at the beginning or at the end" : "Имя пользователя содержит пробел в начале или в конце", "Username must not consist of dots only" : "Имя пользователя должно состоять не только из точек", "A valid password must be provided" : "Укажите допустимый пароль", "The username is already being used" : "Имя пользователя уже используется", "Could not create user" : "Не удалось создать пользователя", "User disabled" : "Пользователь отключен", "Login canceled by app" : "Вход отменен приложением", "No app name specified" : "Не указано имя приложения", "App '%s' could not be installed!" : "Приложение '%s' не может быть установлено!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Приложение «%s» не может быть установлено, так как следующие зависимости не выполнены: %s", "a safe home for all your data" : "надёжный дом для всех ваших данных", "File is currently busy, please try again later" : "Файл в данный момент используется, повторите попытку позже.", "Can't read file" : "Не удается прочитать файл", "Application is not enabled" : "Приложение не разрешено", "Authentication error" : "Ошибка аутентификации", "Token expired. Please reload page." : "Токен просрочен. Перезагрузите страницу.", "Unknown user" : "Неизвестный пользователь", "No database drivers (sqlite, mysql, or postgresql) installed." : "Не установлены драйвера баз данных (sqlite, mysql или postgresql)", "Cannot write into \"config\" directory" : "Запись в каталог «config» невозможна", "Cannot write into \"apps\" directory" : "Запись в каталог «app» невозможна", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог приложений или отключив магазин приложений в файле конфигурации. Смотрите %s", "Cannot create \"data\" directory" : "Невозможно создать каталог «data»", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в корневой каталог. Смотрите %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Разрешения обычно можно исправить, предоставив веб-серверу право на запись в корневой каталог. Смотрите %s.", "Setting locale to %s failed" : "Установка локали %s не удалась", "Please install one of these locales on your system and restart your webserver." : "Установите один из этих языковых пакетов на вашу систему и перезапустите веб-сервер.", "Please ask your server administrator to install the module." : "Пожалуйста, попростите администратора сервера установить модуль.", "PHP module %s not installed." : "Не установлен PHP-модуль %s.", "PHP setting \"%s\" is not set to \"%s\"." : "Параметру PHP «%s» не присвоено значение «%s».", "Adjusting this setting in php.ini will make Nextcloud run again" : "Настройка этого параметра в php.ini поможет Nextcloud работать снова", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload установлен в «%s», при этом требуется «0»", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Чтобы исправить эту проблему установите параметр <code>mbstring.func_overload</code> в значение <code>0</code> в php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Требуется как минимум libxml2 версии 2.7.0. На данный момент установлена %s.", "To fix this issue update your libxml2 version and restart your web server." : "Для исправления этой ошибки обновите версию libxml2 и перезапустите ваш веб-сервер.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Очевидно, PHP настроен на вычищение блоков встроенной документации. Это сделает несколько центральных приложений недоступными.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Возможно это вызвано кешем/ускорителем вроде Zend OPcache или eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Модули PHP были установлены, но они все еще перечислены как недостающие?", "Please ask your server administrator to restart the web server." : "Пожалуйста, попросите вашего администратора перезапустить веб-сервер.", "PostgreSQL >= 9 required" : "Требуется PostgreSQL >= 9", "Please upgrade your database version" : "Обновите базу данных", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Измените права доступа на 0770, чтобы другие пользователи не могли получить список файлов этого каталога.", "Your data directory is readable by other users" : "Каталог данных доступен для чтения другим пользователям", "Your data directory must be an absolute path" : "Каталог данных должен быть указан в виде абсолютного пути", "Check the value of \"datadirectory\" in your configuration" : "Проверьте значение «datadirectory» в настройках.", "Your data directory is invalid" : "Каталог данных не верен", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Убедитесь, что в корне каталога данных присутствует файл «.ocdata».", "Could not obtain lock type %d on \"%s\"." : "Не удалось получить блокировку типа %d для «%s»", "Storage unauthorized. %s" : "Хранилище неавторизовано. %s", "Storage incomplete configuration. %s" : "Неполная конфигурация хранилища. %s", "Storage connection error. %s" : "Ошибка подключения к хранилищу. %s", "Storage is temporarily not available" : "Хранилище временно недоступно", "Storage connection timeout. %s" : "Истекло время ожидания подключения к хранилищу. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Обычно это можно исправить %sпредоставив веб-серверу права на запись в каталоге конфигурации%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модуль с ID %s не существует. Пожалуйста включите его в настройках приложений или обратитесь к администратору.", "Server settings" : "Настройки сервера", "DB Error: \"%s\"" : "Ошибка БД: «%s»", "Offending command was: \"%s\"" : "Вызываемая команда была: «%s»", "You need to enter either an existing account or the administrator." : "Вы должны войти или в существующий аккаунт или под администратором.", "Offending command was: \"%s\", name: %s, password: %s" : "Вызываемая команда была: «%s», имя: %s, пароль: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Не удалось настроить права доступа для %s, указанные права доступа превышают предоставленные для %s", "Setting permissions for %s failed, because the item was not found" : "Не удалось настроить права доступа для %s, элемент не найден.", "Cannot clear expiration date. Shares are required to have an expiration date." : "Невозможно очистить дату истечения срока действия. Общие ресурсы должны иметь срок годности.", "Cannot increase permissions of %s" : "Невозможно увеличить права доступа для %s", "Files can't be shared with delete permissions" : "Файлы не могут иметь общий доступ с правами на удаление", "Files can't be shared with create permissions" : "Файлы не могут иметь общий доступ с правами на создание", "Cannot set expiration date more than %s days in the future" : "Невозможно установить дату окончания срока действия более %s дней", "Personal" : "Личное", "Admin" : "Администрирование", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Обычно это можно исправить, %sпредоставив веб-серверу права на запись в каталог приложений%s или отключив магазин приложений в файле конфигурации.", "Cannot create \"data\" directory (%s)" : "Невозможно создать каталог «data» (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Обычно это можно исправить <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">предоставив веб-серверу права на запись в корневом каталоге</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Обычно это можно исправить, %sпредоставив веб-серверу права на запись в корневой каталог%s.", "Data directory (%s) is readable by other users" : "Каталог данных (%s) доступен для чтения другим пользователям", "Data directory (%s) must be an absolute path" : "Каталог данных (%s) должен быть абсолютным путём", "Data directory (%s) is invalid" : "Каталог данных (%s) не верен", "Please check that the data directory contains a file \".ocdata\" in its root." : "Убедитесь, что файл «.ocdata» присутствует в корне каталога данных." },"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);" } l10n/is.js 0000604 00000055112 15247130447 0006276 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Get ekki skrifað í \"config\" möppuna!", "This can usually be fixed by giving the webserver write access to the config directory" : "Þetta er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í stillingamöppuna", "See %s" : "Skoðaðu %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Þetta er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í stillingamöppuna. Sjá %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Skrám forritsins %$1s var ekki rétt skipt út. Gakktu úr skugga um að þetta sé útgáfa sem sé samhæfð útgáfu vefþjónsins.", "Sample configuration detected" : "Fann sýnisuppsetningu", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Komið hefur í ljós að sýniuppsetningin var afrituð. Þetta getur skemmt uppsetninguna og er ekki stutt. Endilega lestu hjálparskjölin áður en þú gerir breytingar á config.php", "%1$s and %2$s" : "%1$s og %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s og %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s og %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s og %5$s", "Education Edition" : "Kennsluútgáfa", "Enterprise bundle" : "Fyrirtækjavöndull", "Groupware bundle" : "Hópvinnsluvöndull", "Social sharing bundle" : "Deilivöndull fyrir samfélagsmiðla", "PHP %s or higher is required." : "Krafist er PHP %s eða hærra.", "PHP with a version lower than %s is required." : "Krafist er PHP útgáfu %s eða lægri.", "%sbit or higher PHP required." : "Krafist er PHP %sbita eða hærra.", "Following databases are supported: %s" : "Eftirfarandi gagnagrunnar eru studdir: %s", "The command line tool %s could not be found" : "Skipanalínutólið \"%s\" fannst ekki", "The library %s is not available." : "Aðgerðasafnið %s er ekki tiltækt.", "Library %s with a version higher than %s is required - available version %s." : "Krafist er aðgerðasafns %s með útgáfu hærri en %s - tiltæk útgáfa er %s.", "Library %s with a version lower than %s is required - available version %s." : "Krafist er aðgerðasafns %s með útgáfu lægri en %s - tiltæk útgáfa er %s.", "Following platforms are supported: %s" : "Eftirfarandi stýrikerfi eru studd: %s", "Server version %s or higher is required." : "Krafist er þjóns af útgáfu %s eða hærra.", "Server version %s or lower is required." : "Krafist er þjóns af útgáfu %s eða lægri.", "Unknown filetype" : "Óþekkt skráategund", "Invalid image" : "Ógild mynd", "Avatar image is not square" : "Auðkennismynd er ekki ferningslaga", "today" : "í dag", "yesterday" : "í gær", "_%n day ago_::_%n days ago_" : ["fyrir %n degi síðan","fyrir %n dögum síðan"], "last month" : "í síðasta mánuði", "_%n month ago_::_%n months ago_" : ["fyrir %n mánuði","fyrir %n mánuðum"], "last year" : "síðasta ári", "_%n year ago_::_%n years ago_" : ["fyrir %n degi síðan","fyrir %n árum síðan"], "_%n hour ago_::_%n hours ago_" : ["fyrir %n klukkustund síðan","fyrir %n klukkustundum síðan"], "_%n minute ago_::_%n minutes ago_" : ["fyrir %n mínútu síðan","fyrir %n mínútum síðan"], "seconds ago" : "sekúndum síðan", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Eining með auðkenni: %s er ekki til. Virkjaðu hana í forritastillingum eða hafðu samband við kerfisstjóra.", "File name is a reserved word" : "Skráarheiti er þegar frátekið orð", "File name contains at least one invalid character" : "Skráarheitið inniheldur að minnsta kosti einn ógildan staf", "File name is too long" : "Skráarheiti er of langt", "Dot files are not allowed" : "Skrár með punkti eru ekki leyfðar", "Empty filename is not allowed" : "Autt skráarheiti er ekki leyft.", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Ekki er hægt að setja upp \"%s\" forritið vegna þess að ekki var hægt að lesa appinfo-skrána.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Ekki var hægt að setja upp forritið \"%s\" vegna þess að það er ekki samhæft þessari útgáfu vefþjónsins.", "This is an automatically sent email, please do not reply." : "Þetta er sjálfvirk tölvupóstsending, ekki svara þessu.", "Help" : "Hjálp", "Apps" : "Forrit", "Settings" : "Stillingar", "Log out" : "Skrá út", "Users" : "Notendur", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Grunnstillingar", "Sharing" : "Deiling", "Security" : "Öryggi", "Encryption" : "Dulritun", "Additional settings" : "Valfrjálsar stillingar", "Tips & tricks" : "Ábendingar og góð ráð", "Personal info" : "Persónulegar upplýsingar", "Sync clients" : "Samstilla biðlara", "Unlimited" : "Ótakmarkað", "__language_name__" : "Íslenska", "Verifying" : "Sannreyni", "Verifying …" : "Sannreyni …", "Verify" : "Sannreyna", "%s enter the database username and name." : "%s settu inn notandanafn og nafn á gagnagrunni.", "%s enter the database username." : "%s settu inn notandanafn í gagnagrunni.", "%s enter the database name." : "%s settu inn nafn á gagnagrunni.", "%s you may not use dots in the database name" : "%s þú mátt ekki nota punkta í nafni á gagnagrunni", "Oracle connection could not be established" : "Ekki tókst að koma tengingu á við Oracle", "Oracle username and/or password not valid" : "Notandanafn eða lykilorð Oracle er ekki gilt", "PostgreSQL username and/or password not valid" : "Notandanafn eða lykilorð PostgreSQL er ekki gilt", "You need to enter details of an existing account." : "Þú verður að setja inn auðkenni fyrirliggjandi notandaaðgangs.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X er ekki stutt og %s mun ekki vinna eðlilega á þessu stýrikerfi. Notaðu þetta því á þína eigin ábyrgð! ", "For the best results, please consider using a GNU/Linux server instead." : "Fyrir bestu útkomu ættirðu að íhuga að nota GNU/Linux þjón í staðinn.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Það lítur út eins og þessi %s uppsetning sé að keyra á 32-bita PHP umhverfi og að open_basedir hafi verið stillt í php.ini. Þetta mun valda vandamálum með skrár stærri en 4 GB og er stranglega mælt gegn því að þetta sé gert.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Fjarlægðu stillinguna open_basedir úr php.ini eða skiptu yfir í 64-bita PHP.", "Set an admin username." : "Stilltu notandanafn kerfisstjóra.", "Set an admin password." : "Stilltu lykilorð kerfisstjóra.", "Can't create or write into the data directory %s" : "Gat ekki búið til eða skrifað í gagnamöppuna %s", "Invalid Federated Cloud ID" : "Ógilt skýjasambandsauðkenni (Federated Cloud ID)", "Sharing %s failed, because the backend does not allow shares from type %i" : "Deiling %s mistókst, því bakvinnslukerfið leyfir ekki sameignir af gerðinni %i", "Sharing %s failed, because the file does not exist" : "Deiling %s mistókst, því skráin er ekki til", "You are not allowed to share %s" : "Þú hefur ekki heimild til að deila %s", "Sharing %s failed, because you can not share with yourself" : "Deiling %s mistókst, því þú getur ekki deilt með sjálfum þér", "Sharing %s failed, because the user %s does not exist" : "Deiling %s mistókst, því notandinn %s er ekki til", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Deiling %s mistókst, því notandinn %s er ekki meðlimur í neinum hópi sem %s er meðlimur í", "Sharing %s failed, because this item is already shared with %s" : "Deiling %s mistókst, því þessu atriði er þegar deilt með %s", "Sharing %s failed, because this item is already shared with user %s" : "Deiling %s mistókst, því þessu atriði er þegar deilt með notandanum %s", "Sharing %s failed, because the group %s does not exist" : "Deiling %s mistókst, því hópurinn %s er ekki til", "Sharing %s failed, because %s is not a member of the group %s" : "Deiling %s mistókst, því %s er ekki meðlimur í hópnum %s", "You need to provide a password to create a public link, only protected links are allowed" : "Þú verður að setja inn lykilorð til að útbúa opinberan tengil, aðeins verndaðir tenglar eru leyfðir", "Sharing %s failed, because sharing with links is not allowed" : "Deiling %s mistókst, því deiling með tenglum er ekki leyfð", "Not allowed to create a federated share with the same user" : "Ekki er heimilt að búa til skýjasambandssameign með sama notanda", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Deiling %s mistókst, gat ekki fundið %s, hugsanlega er þjónninn ekki tiltækur í augnablikinu.", "Share type %s is not valid for %s" : "Deiling af gerðinni %s er ekki gild fyrir %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Get ekki stillt gildistímann. Sameignir geta ekki runnið út síðar en %s eftir að þeim hefur verið deilt", "Cannot set expiration date. Expiration date is in the past" : "Get ekki stillt gildistímann. Gildistíminn er þegar runninn út", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Deilingarbakendinn %s verður að vera settur upp fyrir viðmótið OCP\\Share_Backend", "Sharing backend %s not found" : "Deilingarbakendinn %s fannst ekki", "Sharing backend for %s not found" : "Deilingarbakendi fyrir %s fannst ekki", "Sharing failed, because the user %s is the original sharer" : "Deiling mistókst, því notandinn %s er upprunalegur deilandi", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Deiling %s mistókst, því heimildirnar eru rétthærri en heimildir til handa %s", "Sharing %s failed, because resharing is not allowed" : "Deiling %s mistókst, því endurdeiling er ekki leyfð", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Deiling %s mistókst, því bakvinnslukerfið fyrir %s fann ekki upptök þess", "Sharing %s failed, because the file could not be found in the file cache" : "Deiling %s mistókst, því skráin fannst ekki í skyndiminni skráa", "Can’t increase permissions of %s" : "Get ekki aukið aðgangsheimildir %s", "Files can’t be shared with delete permissions" : "Ekki er hægt að deila skrá með eyða-heimildum", "Files can’t be shared with create permissions" : "Ekki er hægt að deila skrá með búa-til-heimildum", "Expiration date is in the past" : "Gildistíminn er þegar runninn út", "Can’t set expiration date more than %s days in the future" : "Ekki er hægt að setja lokadagsetningu meira en %s daga fram í tímann", "%s shared »%s« with you" : "%s deildi »%s« með þér", "%s shared »%s« with you." : "%s deildi »%s« með þér.", "Click the button below to open it." : "Smelltu á hnappinn hér fyrir neðan til að opna það.", "Open »%s«" : "Opna »%s«", "%s via %s" : "%s með %s", "The requested share does not exist anymore" : "Umbeðin sameign er ekki lengur til", "Could not find category \"%s\"" : "Fann ekki flokkinn \"%s\"", "Sunday" : "Sunnudagur", "Monday" : "Mánudagur", "Tuesday" : "Þriðjudagur", "Wednesday" : "Miðvikudagur", "Thursday" : "Fimmtudagur", "Friday" : "Föstudagur", "Saturday" : "Laugardagur", "Sun." : "Sun.", "Mon." : "Mán.", "Tue." : "Þri.", "Wed." : "Mið.", "Thu." : "Fim.", "Fri." : "Fös.", "Sat." : "Lau.", "Su" : "Su", "Mo" : "Má", "Tu" : "Þr", "We" : "Mi", "Th" : "Fi", "Fr" : "Fö", "Sa" : "La", "January" : "Janúar", "February" : "Febrúar", "March" : "Mars", "April" : "Apríl", "May" : "Maí", "June" : "Júní", "July" : "Júlí", "August" : "Ágúst", "September" : "September", "October" : "Október", "November" : "Nóvember", "December" : "Desember", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Maí.", "Jun." : "Jún.", "Jul." : "Júl.", "Aug." : "Ágú.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nóv.", "Dec." : "Des.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Einungis eru leyfilegir eftirfarandi stafir í notandanafni: \"a-z\", \"A-Z\", \"0-9\", og \"_.@-'\"", "A valid username must be provided" : "Skráðu inn gilt notandanafn", "Username contains whitespace at the beginning or at the end" : "Notandanafnið inniheldur orðabil í upphafi eða enda", "Username must not consist of dots only" : "Notandanafn má ekki einungis samanstanda af punktum", "A valid password must be provided" : "Skráðu inn gilt lykilorð", "The username is already being used" : "Notandanafnið er þegar í notkun", "Could not create user" : "Gat ekki búið til notanda", "User disabled" : "Notandi óvirkur", "Login canceled by app" : "Forrit hætti við innskráningu", "No app name specified" : "Ekkert heiti forrits tilgreint", "App '%s' could not be installed!" : "Ekki var hægt að setja upp '%s' forritið!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Ekki var hægt að setja upp \"%s\" forritið þar sem eftirfarandi kerfiskröfur eru ekki uppfylltar: %s", "a safe home for all your data" : "öruggur staður fyrir öll gögnin þín", "File is currently busy, please try again later" : "Skráin er upptekin í augnablikinu, reyndu aftur síðar", "Can't read file" : "Get ekki lesið skrána", "Application is not enabled" : "Forrit ekki virkt", "Authentication error" : "Villa við auðkenningu", "Token expired. Please reload page." : "Kenniteikn er útrunnið. Þú ættir að hlaða síðunni aftur inn.", "Unknown user" : "Óþekktur notandi", "No database drivers (sqlite, mysql, or postgresql) installed." : "Engir reklar fyrir gagnagrunn eru uppsettir (sqlite, mysql eða postgresql).", "Cannot write into \"config\" directory" : "Get ekki skrifað í \"config\" möppuna", "Cannot write into \"apps\" directory" : "Get ekki skrifað í \"apps\" möppuna", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Þetta er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í forritamöppuna með því að gera forritabúðina óvirka í stillingaskránni. Sjá %s", "Cannot create \"data\" directory" : "Get ekki búið til \"data\" möppu", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Þetta er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í rótarmöppuna. Sjá %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Heimildir er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í rótarmöppuna. Sjá %s", "Setting locale to %s failed" : "Mistókst að setja upp staðfærsluna %s", "Please install one of these locales on your system and restart your webserver." : "Settu upp eina af þessum staðfærslum og endurræstu vefþjóninn.", "Please ask your server administrator to install the module." : "Biddu kerfisstjórann þinn um að setja eininguna upp.", "PHP module %s not installed." : "PHP-einingin %s er ekki uppsett.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-stillingin \"%s\" er ekki sett á \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ef þessi stilling er löguð í php.ini mun Nextcloud keyra aftur", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload er stillt á \"%s\" í stað gildisins \"0\" eins og vænst var", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Til að laga þetta vandamál ættirðu að setja <code>mbstring.func_overload</code> sem <code>0</code> í php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Krafist er libxml2 2.7.0 hið minnsta. Núna er %s uppsett.", "To fix this issue update your libxml2 version and restart your web server." : "Til að laga þetta vandamál ættirðu að uppfæra útgáfu þína af libxml2 og endurræsa vefþjóninn.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP virðist vera sett upp to fjarlægja innantextablokkir (inline doc blocks). Þetta mun gera ýmis kjarnaforrit óaðgengileg.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Þessu veldur væntanlega biðminni/hraðall á borð við Zend OPcache eða eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Búið er að setja upp PHP-einingar, en eru þær ennþá taldar upp eins og þær vanti?", "Please ask your server administrator to restart the web server." : "Biddu kerfisstjórann þinn um að endurræsa vefþjóninn.", "PostgreSQL >= 9 required" : "Krefst PostgreSQL >= 9", "Please upgrade your database version" : "Uppfærðu útgáfu gagnagrunnsins", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Endilega breyttu heimildunum í 0770 svo að aðrir notendur geti ekki listað upp innihald hennar.", "Your data directory is readable by other users" : "Gagnamappn þín er lesanleg fyrir aðra notendur", "Your data directory must be an absolute path" : "Gagnamappan þín verður að vera með algilda slóð", "Check the value of \"datadirectory\" in your configuration" : "Athugaðu gildi \"datadirectory\" í uppsetningunni þinni", "Your data directory is invalid" : "Gagnamappan þín er ógild", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Gakktu úr skugga um að til staðar sé skrá með heitinu \".ocdata\" í rót gagnageymslunnar.", "Could not obtain lock type %d on \"%s\"." : "Gat ekki fengið læsingu af gerðinni %d á \"%s\".", "Storage unauthorized. %s" : "Gagnageymsla ekki auðkennd. %s", "Storage incomplete configuration. %s" : "Ófullgerð uppsetning gagnageymslu. %s", "Storage connection error. %s" : "Villa í tengingu við gagnageymslu. %s", "Storage is temporarily not available" : "Gagnageymsla ekki tiltæk í augnablikinu", "Storage connection timeout. %s" : "Gagnageymsla féll á tíma. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Þetta er venjulega hægt að laga ef %sgefur vefþjóninum skrifréttindi í stillingamöppuna%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Eining með auðkenni: %s er ekki til. Virkjaðu hana í forritastillingum eða hafðu samband við kerfisstjóra.", "Server settings" : "Stillingar þjóns", "DB Error: \"%s\"" : "Gagnagrunnsvilla: \"%s\"", "Offending command was: \"%s\"" : "Saknæma skipunin var: \"%s\"", "You need to enter either an existing account or the administrator." : "Þú verður að setja inn fyrirliggjandi notandaaðgang eða kerfisstjóra.", "Offending command was: \"%s\", name: %s, password: %s" : "Saknæma skipunin var: \"%s\", nafn: %s, lykilorð: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Stilling heimilda fyrir %s mistókst, því heimildirnar eru rétthærri en heimildir til handa %s", "Setting permissions for %s failed, because the item was not found" : "Stilling heimilda fyrir %s mistókst, því atriðið fannst ekki", "Cannot clear expiration date. Shares are required to have an expiration date." : "Get ekki hreinsað út gildistímann. Ætlast er til þess að sameignir hafi ákveðinn gildistíma.", "Cannot increase permissions of %s" : "Get ekki aukið aðgangsheimildir %s", "Files can't be shared with delete permissions" : "Ekki er hægt að deila skrá með eyða-heimildum", "Files can't be shared with create permissions" : "Ekki er hægt að deila skrá með búa-til-heimildum", "Cannot set expiration date more than %s days in the future" : "Ekki er hægt að setja lokadagsetningu meira en %s daga fram í tímann", "Personal" : "Einka", "Admin" : "Stjórnun", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Þetta er venjulega hægt að laga ef %sgefur vefþjóninum skrifréttindi í forritamöppuna%s eða gerir forritabúðina óvirka í stillingaskránni.", "Cannot create \"data\" directory (%s)" : "Get ekki búið til \"data\" möppu (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Þetta er venjulega hægt að laga ef <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">gefur vefþjóninum skrifréttindi í rótarmöppuna </a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Heimildir er venjulega hægt að laga ef %sgefur vefþjóninum skrifréttindi í rótarmöppuna %s.", "Data directory (%s) is readable by other users" : "Gagnamappa (%s) er lesanleg fyrir aðra notendur", "Data directory (%s) must be an absolute path" : "Gagnamappan (%s) verður að vera algild slóð", "Data directory (%s) is invalid" : "Gagnamappa (%s) er ógild", "Please check that the data directory contains a file \".ocdata\" in its root." : "Athugaðu hvort gagnamappan innihaldi skrá með heitinu \".ocdata\" í rót hennar." }, "nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"); l10n/de.json 0000604 00000057300 15247130447 0006611 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Das Schreiben in das „config“-Verzeichnis ist nicht möglich!", "This can usually be fixed by giving the webserver write access to the config directory" : "Dies kann normalerweise repariert werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird", "See %s" : "Siehe %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Die Dateien der App %$1s wurden nicht korrekt ersetzt. Stelle sicher, dass die Version mit dem Server kompatibel ist.", "Sample configuration detected" : "Beispielkonfiguration gefunden", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Es wurde festgestellt, dass die Beispielkonfiguration kopiert wurde. Dies kann Deine Installation zerstören und wird nicht unterstützt. Bitte die Dokumentation lesen, bevor Änderungen an der config.php vorgenommen werden.", "%1$s and %2$s" : "%1$s und %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s und %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s und %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s und %5$s", "Education Edition" : "Bildungsausgabe", "Enterprise bundle" : "Firmen-Paket", "Groupware bundle" : "Groupware-Paket", "Social sharing bundle" : "Paket für das Teilen in sozialen Medien", "PHP %s or higher is required." : "PHP %s oder höher wird benötigt.", "PHP with a version lower than %s is required." : "PHP wird in einer früheren Version als %s benötigt.", "%sbit or higher PHP required." : "%sbit oder höheres PHP wird benötigt.", "Following databases are supported: %s" : "Die folgenden Datenbanken werden unterstützt: %s", "The command line tool %s could not be found" : "Das Kommandozeilenwerkzeug %s konnte nicht gefunden werden", "The library %s is not available." : "Die Bibliothek %s ist nicht verfügbar.", "Library %s with a version higher than %s is required - available version %s." : "Die Bibliothek %s wird in einer neueren Version als %s benötigt - verfügbare Version ist %s.", "Library %s with a version lower than %s is required - available version %s." : "Die Bibliothek %s wird in einer früheren Version als %s benötigt - verfügbare Version ist %s.", "Following platforms are supported: %s" : "Die folgenden Plattformen werden unterstützt: %s", "Server version %s or higher is required." : "Server Version %s oder höher wird benötigt.", "Server version %s or lower is required." : "Server Version %s oder niedriger wird benötigt.", "Unknown filetype" : "Unbekannter Dateityp", "Invalid image" : "Ungültiges Bild", "Avatar image is not square" : "Benutzerbild ist nicht quadratisch", "today" : "Heute", "yesterday" : "Gestern", "_%n day ago_::_%n days ago_" : ["Vor %n Tag","Vor %n Tagen"], "last month" : "Letzten Monat", "_%n month ago_::_%n months ago_" : ["Vor %n Monat","Vor %n Monaten"], "last year" : "Letztes Jahr", "_%n year ago_::_%n years ago_" : ["Vor %n Jahr","Vor %n Jahren"], "_%n hour ago_::_%n hours ago_" : ["Vor %n Stunde","Vor %n Stunden"], "_%n minute ago_::_%n minutes ago_" : ["Vor %n Minute","Vor %n Minuten"], "seconds ago" : "Gerade eben", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte die App in den App-Einstellungen aktivieren oder den Administrator kontaktieren.", "File name is a reserved word" : "Der Dateiname ist ein reserviertes Wort", "File name contains at least one invalid character" : "Der Dateiname enthält mindestens ein ungültiges Zeichen", "File name is too long" : "Dateiname ist zu lang", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", "Empty filename is not allowed" : "Ein leerer Dateiname ist nicht erlaubt", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Die Anwendung \"%s\" kann nicht installiert werden, weil die Anwendungsinfodatei nicht gelesen werden kann.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Die App \"%s\" kann nicht installiert werden, da sie mit dieser Serverversion nicht kompatibel ist.", "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.", "Help" : "Hilfe", "Apps" : "Apps", "Settings" : "Einstellungen", "Log out" : "Abmelden", "Users" : "Benutzer", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Grundeinstellungen", "Sharing" : "Teilen", "Security" : "Sicherheit", "Encryption" : "Verschlüsselung", "Additional settings" : "Zusätzliche Einstellungen", "Tips & tricks" : "Tipps & Tricks", "Personal info" : "Persönliche Informationen ", "Sync clients" : " Sync-Clients ", "Unlimited" : "Unbegrenzt", "__language_name__" : " Deutsch (Persönlich: Du) ", "Verifying" : "Überprüfe", "Verifying …" : " Überprüfe… ", "Verify" : "Überprüfen", "%s enter the database username and name." : "%s gebe den Datenbank-Benutzernamen und den Datenbanknamen ein.", "%s enter the database username." : "%s gebe den Datenbank-Benutzernamen an.", "%s enter the database name." : "%s gebe den Datenbanknamen an.", "%s you may not use dots in the database name" : "%s Der Datenbankname darf keine Punkte enthalten", "Oracle connection could not be established" : "Es konnte keine Verbindung zur Oracle-Datenbank hergestellt werden", "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "You need to enter details of an existing account." : "Du musst Details von einem existierenden Benutzer einfügen.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", "For the best results, please consider using a GNU/Linux server instead." : "Zur Gewährleistung eines optimalen Betriebs sollte stattdessen ein GNU/Linux-Server verwendet werden.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Es scheint, dass diese %s-Instanz unter einer 32-Bit-PHP-Umgebung läuft und open_basedir in der Datei php.ini konfiguriert worden ist. Von einem solchen Betrieb wird dringend abgeraten, weil es dabei zu Problemen mit Dateien kommt, deren Größe 4 GB übersteigt.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Bitte entferne die open_basedir-Einstellung in Deiner php.ini oder wechsele zu 64-Bit-PHP.", "Set an admin username." : "Einen Administrator-Benutzernamen setzen.", "Set an admin password." : "Ein Administrator-Passwort setzen.", "Can't create or write into the data directory %s" : "Das Datenverzeichnis %s kann nicht erstellt oder es kann darin nicht geschrieben werden.", "Invalid Federated Cloud ID" : "Ungültige Federated-Cloud-ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Freigabe von %s fehlgeschlagen, da das Backend die Freigabe vom Typ %i nicht erlaubt.", "Sharing %s failed, because the file does not exist" : "Freigabe von %s fehlgeschlagen, da die Datei nicht existiert", "You are not allowed to share %s" : "Die Freigabe von %s ist Dir nicht erlaubt", "Sharing %s failed, because you can not share with yourself" : "Freigabe von %s fehlgeschlagen, da du nichts mit dir selbst teilen kannst", "Sharing %s failed, because the user %s does not exist" : "Freigabe von %s fehlgeschlagen, da der Benutzer %s nicht existiert", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Freigabe von %s fehlgeschlagen, da der Benutzer %s kein Gruppenmitglied einer der Gruppen von %s ist", "Sharing %s failed, because this item is already shared with %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit %s geteilt wird", "Sharing %s failed, because this item is already shared with user %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit dem Benutzer %s geteilt wird", "Sharing %s failed, because the group %s does not exist" : "Freigabe von %s fehlgeschlagen, da die Gruppe %s nicht existiert", "Sharing %s failed, because %s is not a member of the group %s" : "Freigabe von %s fehlgeschlagen, da %s kein Mitglied der Gruppe %s ist", "You need to provide a password to create a public link, only protected links are allowed" : "Es sind nur geschützte Links zulässig, daher musst Du ein Passwort angeben, um einen öffentlichen Link zu generieren", "Sharing %s failed, because sharing with links is not allowed" : "Freigabe von %s fehlgeschlagen, da das Teilen von Verknüpfungen nicht erlaubt ist", "Not allowed to create a federated share with the same user" : "Das Erstellen einer Federated-Cloud-Freigabe mit dem gleichen Benutzer ist nicht erlaubt", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Freigabe von %s fehlgeschlagen, da %s nicht gefunden wurde. Möglicherweise ist der Server nicht erreichbar.", "Share type %s is not valid for %s" : "Freigabetyp %s ist nicht gültig für %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Ablaufdatum kann nicht gesetzt werden. Freigaben können nach dem Teilen, nicht länger als %s gültig sein.", "Cannot set expiration date. Expiration date is in the past" : "Ablaufdatum kann nicht gesetzt werden. Ablaufdatum liegt in der Vergangenheit.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Freigabe-Backend %s muss in der OCP\\Share_Backend - Schnittstelle implementiert werden", "Sharing backend %s not found" : "Freigabe-Backend %s nicht gefunden", "Sharing backend for %s not found" : "Freigabe-Backend für %s nicht gefunden", "Sharing failed, because the user %s is the original sharer" : "Freigabe fehlgeschlagen, da der Benutzer %s der ursprünglich Teilende ist", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Freigabe von %s fehlgeschlagen, da die Berechtigungen die erteilten Berechtigungen %s überschreiten", "Sharing %s failed, because resharing is not allowed" : "Freigabe von %s fehlgeschlagen, da das nochmalige Freigeben einer Freigabe nicht erlaubt ist", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Freigabe von %s fehlgeschlagen, da das Freigabe-Backend für %s nicht in dieser Quelle gefunden werden konnte", "Sharing %s failed, because the file could not be found in the file cache" : "Freigabe von %s fehlgeschlagen, da die Datei im Datei-Cache nicht gefunden werden konnte", "Can’t increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen", "Files can’t be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden", "Files can’t be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden", "Expiration date is in the past" : "Das Ablaufdatum liegt in der Vergangenheit.", "Can’t set expiration date more than %s days in the future" : "Das Ablaufdatum kann nicht mehr als %s Tage in die Zukunft liegen", "%s shared »%s« with you" : "%s hat „%s“ mit Dir geteilt", "%s shared »%s« with you." : "%s hat mit Dir »%s« geteilt.", "Click the button below to open it." : "Klicke zum Öffnen auf die untere Schaltfläche.", "Open »%s«" : "»%s« öffnen", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Die angeforderte Freigabe existiert nicht mehr", "Could not find category \"%s\"" : "Die Kategorie \"%s“ konnte nicht gefunden werden", "Sunday" : "Sonntag", "Monday" : "Montag", "Tuesday" : "Dienstag", "Wednesday" : "Mittwoch", "Thursday" : "Donnerstag", "Friday" : "Freitag", "Saturday" : "Samstag", "Sun." : "Son.", "Mon." : "Mon.", "Tue." : "Die.", "Wed." : "Mit.", "Thu." : "Don.", "Fri." : "Fre.", "Sat." : "Sam.", "Su" : "So", "Mo" : "Mo", "Tu" : "Di", "We" : "Mi", "Th" : "Do", "Fr" : "Fr", "Sa" : "Sa", "January" : "Januar", "February" : "Februar", "March" : "März", "April" : "April", "May" : "Mai", "June" : "Juni", "July" : "Juli", "August" : "August", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "Dezember", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mär.", "Apr." : "Apr.", "May." : "Mai", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dez.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Folgende Zeichen sind im Benutzernamen erlaubt: „a-z“, „A-Z“, „0-9“ und „_.@-'“", "A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden", "Username contains whitespace at the beginning or at the end" : "Der Benutzername enthält Leerzeichen am Anfang oder am Ende", "Username must not consist of dots only" : "Der Benutzername darf nicht nur aus Punkten bestehen", "A valid password must be provided" : "Es muss ein gültiges Passwort eingegeben werden", "The username is already being used" : "Dieser Benutzername existiert bereits", "Could not create user" : "Benutzer konnte nicht erstellt werden", "User disabled" : "Nutzer deaktiviert", "Login canceled by app" : "Anmeldung durch die App abgebrochen", "No app name specified" : "Es wurde kein App-Name angegeben", "App '%s' could not be installed!" : "'%s' - App konnte nicht installiert werden!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Die App „%s“ kann nicht installiert werden, da die folgenden Abhängigkeiten nicht erfüllt sind: %s", "a safe home for all your data" : "ein sicherer Ort für all Deine Daten", "File is currently busy, please try again later" : "Die Datei ist in Benutzung, bitte versuche es später noch einmal", "Can't read file" : "Datei kann nicht gelesen werden", "Application is not enabled" : "Die Anwendung ist nicht aktiviert", "Authentication error" : "Authentifizierungsfehler", "Token expired. Please reload page." : "Token abgelaufen. Bitte lade die Seite neu.", "Unknown user" : "Unbekannter Benutzer", "No database drivers (sqlite, mysql, or postgresql) installed." : "Keine Datenbanktreiber (SQLite, MySQL oder PostgreSQL) installiert.", "Cannot write into \"config\" directory" : "Schreiben in das „config“-Verzeichnis ist nicht möglich", "Cannot write into \"apps\" directory" : "Schreiben in das „apps“-Verzeichnis ist nicht möglich", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das App-Verzeichnis eingeräumt wird. Siehe auch %s", "Cannot create \"data\" directory" : "Kann das \"Daten\"-Verzeichnis nicht erstellen", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Berechtigungen können zumeist korrigiert werden indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s.", "Setting locale to %s failed" : "Das Setzen der Umgebungslokale auf %s ist fehlgeschlagen", "Please install one of these locales on your system and restart your webserver." : "Bitte installiere eine dieser Sprachen auf Deinem System und starte den Webserver neu.", "Please ask your server administrator to install the module." : "Bitte für die Installation des Moduls Deinen Server-Administrator kontaktieren.", "PHP module %s not installed." : "PHP-Modul %s nicht installiert.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-Einstellung „%s“ ist nicht auf „%s“ gesetzt.", "Adjusting this setting in php.ini will make Nextcloud run again" : "Eine Änderung dieser Einstellung in der php.ini kann deine Nextcloud wieder lauffähig machen.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload ist nicht auf den erwarteten Wert „0“, sondern stattdessen auf „%s“ gesetzt", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in Deiner php.ini auf <code>0</code>.", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 ist mindestestens erforderlich. Im Moment ist %s installiert.", "To fix this issue update your libxml2 version and restart your web server." : "Um den Fehler zu beheben, musst Du die libxml2 Version aktualisieren und den Webserver neustarten.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ist offenbar so konfiguriert, dass PHPDoc-Blöcke in der Anweisung entfernt werden. Dadurch sind mehrere Kern-Apps nicht erreichbar.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dies wird wahrscheinlich durch Zwischenspeicher/Beschleuniger wie etwa Zend OPcache oder eAccelerator verursacht.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-Module wurden installiert, werden aber als noch fehlend gelistet?", "Please ask your server administrator to restart the web server." : "Bitte kontaktiere Deinen Server-Administrator und bitte um den Neustart des Webservers.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 benötigt", "Please upgrade your database version" : "Bitte aktualisiere deine Datenbankversion", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Bitte ändere die Berechtigungen auf 0770, sodass das Verzeichnis nicht von anderen Benutzern angezeigt werden kann.", "Your data directory is readable by other users" : "Dein Datenverzeichnis kann von anderen Benutzern gelesen werden", "Your data directory must be an absolute path" : "Dein Datenverzeichnis muss einen eindeutigen Pfad haben", "Check the value of \"datadirectory\" in your configuration" : "Überprüfe bitte die Angabe unter „datadirectory“ in Deiner Konfiguration", "Your data directory is invalid" : "Dein Datenverzeichnis ist ungültig", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Stelle sicher, dass eine Datei \".ocdata\" im Wurzelverzeichnis des data-Verzeichnisses existiert.", "Could not obtain lock type %d on \"%s\"." : "Sperrtyp %d auf „%s“ konnte nicht ermittelt werden.", "Storage unauthorized. %s" : "Speicher nicht authorisiert. %s", "Storage incomplete configuration. %s" : "Speicher-Konfiguration unvollständig. %s", "Storage connection error. %s" : "Verbindungsfehler zum Speicherplatz. %s", "Storage is temporarily not available" : "Speicher ist vorübergehend nicht verfügbar", "Storage connection timeout. %s" : "Zeitüberschreitung der Verbindung zum Speicherplatz. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Dies kann normalerweise behoben werden, %sindem dem Webserver Schreibzugriff auf das Konfigurationsverzeichnis gegeben wird %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte die Aktivierung in Deinen App-Einstellungen vornehmen oder Deine Administrator kontaktieren.", "Server settings" : "Servereinstellungen", "DB Error: \"%s\"" : "DB-Fehler: „%s“", "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", "You need to enter either an existing account or the administrator." : "Du musst entweder ein existierendes Benutzerkonto oder das Administratorenkonto angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Das Setzen der Berechtigungen für %s ist fehlgeschlagen, da die neuen Berechtigungen, die erteilten Berechtigungen %s überschreiten", "Setting permissions for %s failed, because the item was not found" : "Das Setzen der Berechtigungen für %s ist fehlgeschlagen, da das Element nicht gefunden wurde", "Cannot clear expiration date. Shares are required to have an expiration date." : "Ablaufdatum kann nicht gelöscht werden. Freigaben werden für ein Ablaufdatum benötigt.", "Cannot increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen", "Files can't be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden", "Files can't be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden", "Cannot set expiration date more than %s days in the future" : "Das Ablaufdatum kann nicht mehr als %s Tage in die Zukunft liegen", "Personal" : "Persönlich", "Admin" : "Verwaltung", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Dies kann normalerweise behoben werden, %sindem dem Webserver Schreibzugriff auf das App-Verzeichnis gegeben wird%s oder der App Store in der Konfigurationsdatei deaktiviert wird.", "Cannot create \"data\" directory (%s)" : "Erstellen des „data“-Verzeichnisses ist nicht möglich (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Dies kann normalerweise repariert werden, indem dem Webserver <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> Schreibzugriff auf das Wurzelverzeichnis gegeben wird</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Berechtigungen können normalerweise repariert werden, indem dem Webserver %s Schreibzugriff auf das Wurzelverzeichnis %s gegeben wird.", "Data directory (%s) is readable by other users" : "Datenverzeichnis (%s) ist von anderen Nutzern lesbar", "Data directory (%s) must be an absolute path" : "Das Datenverzeichnis (%s) muss ein absoluter Pfad sein", "Data directory (%s) is invalid" : "Datenverzeichnis (%s) ist ungültig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Bitte stelle sicher, dass das Datenverzeichnis auf seiner ersten Ebene eine Datei namens „.ocdata“ enthält." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/it.json 0000604 00000055574 15247130447 0006650 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Impossibile scrivere nella cartella \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella \"config\"", "See %s" : "Vedi %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella di configurazione. Vedi %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "I file dell'applicazione %1$s non sono stati sostituiti correttamente. Assicurati che sia una versione compatibile con il server.", "Sample configuration detected" : "Configurazione di esempio rilevata", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "È stato rilevato che la configurazione di esempio è stata copiata. Ciò può compromettere la tua installazione e non è supportato. Leggi la documentazione prima di modificare il file config.php", "%1$s and %2$s" : "%1$s e %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s e %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s e %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s e %5$s", "Education Edition" : "Edizione didattica", "Enterprise bundle" : "Pacchetto Enterprise", "Groupware bundle" : "Pacchetto Groupware", "Social sharing bundle" : "Pacchetto Social sharing", "PHP %s or higher is required." : "Richiesto PHP %s o superiore", "PHP with a version lower than %s is required." : "Richiesta una versione di PHP minore di %s.", "%sbit or higher PHP required." : "Richiesto PHP %sbit o superiore.", "Following databases are supported: %s" : "I seguenti database sono supportati: %s", "The command line tool %s could not be found" : "Lo strumento da riga di comando %s non è stato trovato", "The library %s is not available." : "La libreria %s non è disponibile.", "Library %s with a version higher than %s is required - available version %s." : "Richiesta una versione della libreria %s maggiore di %s - versione disponibile %s.", "Library %s with a version lower than %s is required - available version %s." : "Richiesta una versione della libreria %s minore di %s - versione disponibile %s.", "Following platforms are supported: %s" : "Sono supportate le seguenti piattaforme: %s", "Server version %s or higher is required." : "È richiesta la versione %s o successiva.", "Server version %s or lower is required." : "È richiesta la versione %s o precedente.", "Unknown filetype" : "Tipo di file sconosciuto", "Invalid image" : "Immagine non valida", "Avatar image is not square" : "L'immagine personale non è quadrata", "today" : "oggi", "yesterday" : "ieri", "_%n day ago_::_%n days ago_" : ["%d giorno fa","%n giorni fa"], "last month" : "mese scorso", "_%n month ago_::_%n months ago_" : ["%n mese fa","%n mesi fa"], "last year" : "anno scorso", "_%n year ago_::_%n years ago_" : ["%n anno fa","%n anni fa"], "_%n hour ago_::_%n hours ago_" : ["%n ora fa","%n ore fa"], "_%n minute ago_::_%n minutes ago_" : ["%n minuto fa","%n minuti fa"], "seconds ago" : "secondi fa", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Il modulo con ID: %s non esiste. Abilitalo nelle impostazioni delle applicazioni o contatta il tuo amministratore.", "File name is a reserved word" : "Il nome del file è una parola riservata", "File name contains at least one invalid character" : "Il nome del file contiene almeno un carattere non valido", "File name is too long" : "Il nome del file è troppo lungo", "Dot files are not allowed" : "I file con un punto iniziale non sono consentiti", "Empty filename is not allowed" : "Un nome di file vuoto non è consentito", "App \"%s\" cannot be installed because appinfo file cannot be read." : "L'applicazione \"%s\" non può essere installata poiché il file appinfo non può essere letto.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "L'applicazione \"%s\" non può essere installata perché non è compatibile con questa versione del server.", "This is an automatically sent email, please do not reply." : "Questo è un messaggio di posta inviato automaticamente, non rispondere.", "Help" : "Aiuto", "Apps" : "Applicazioni", "Settings" : "Impostazioni", "Log out" : "Esci", "Users" : "Utenti", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Impostazioni di base", "Sharing" : "Condivisione", "Security" : "Sicurezza", "Encryption" : "Cifratura", "Additional settings" : "Impostazioni aggiuntive", "Tips & tricks" : "Suggerimenti e trucchi", "Personal info" : "Informazioni personali", "Sync clients" : "Client di sincronizzazione", "Unlimited" : "Illimitato", "__language_name__" : "Italiano", "Verifying" : "Verifica", "Verifying …" : "Verifica in corso...", "Verify" : "Verifica", "%s enter the database username and name." : "%s digita il nome utente e il nome del database.", "%s enter the database username." : "%s digita il nome utente del database.", "%s enter the database name." : "%s digita il nome del database.", "%s you may not use dots in the database name" : "%s non dovresti utilizzare punti nel nome del database", "Oracle connection could not be established" : "La connessione a Oracle non può essere stabilita", "Oracle username and/or password not valid" : "Nome utente e/o password di Oracle non validi", "PostgreSQL username and/or password not valid" : "Nome utente e/o password di PostgreSQL non validi", "You need to enter details of an existing account." : "Devi inserire i dettagli di un account esistente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X non è supportato e %s non funzionerà correttamente su questa piattaforma. Usalo a tuo rischio!", "For the best results, please consider using a GNU/Linux server instead." : "Per avere il risultato migliore, prendi in considerazione l'utilizzo di un server GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Sembra che questa istanza di %s sia in esecuzione in un ambiente PHP a 32 bit e che open_basedir sia stata configurata in php.ini. Ciò comporterà problemi con i file più grandi di 4 GB ed è altamente sconsigliato.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Rimuovi l'impostazione di open_basedir nel tuo php.ini o passa alla versione a 64 bit di PHP.", "Set an admin username." : "Imposta un nome utente di amministrazione.", "Set an admin password." : "Imposta una password di amministrazione.", "Can't create or write into the data directory %s" : "Impossibile creare o scrivere nella cartella dei dati %s", "Invalid Federated Cloud ID" : "ID di cloud federata non valido", "Sharing %s failed, because the backend does not allow shares from type %i" : "Condivisione di %s non riuscita, poiché il motore non consente condivisioni del tipo %i", "Sharing %s failed, because the file does not exist" : "Condivisione di %s non riuscita, poiché il file non esiste", "You are not allowed to share %s" : "Non ti è consentito condividere %s", "Sharing %s failed, because you can not share with yourself" : "Condivisione di %s non riuscita, poiché non puoi condividere con te stesso", "Sharing %s failed, because the user %s does not exist" : "Condivisione di %s non riuscita, poiché l'utente %s non esiste", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Condivisione di %s non riuscita, poiché l'utente %s non appartiene ad alcun gruppo di cui %s è membro", "Sharing %s failed, because this item is already shared with %s" : "Condivisione di %s non riuscita, poiché l'oggetto è già condiviso con %s", "Sharing %s failed, because this item is already shared with user %s" : "Condivisione di %s non riuscita, poiché l'oggetto è già condiviso con l'utente %s", "Sharing %s failed, because the group %s does not exist" : "Condivisione di %s non riuscita, poiché il gruppo %s non esiste", "Sharing %s failed, because %s is not a member of the group %s" : "Condivisione di %s non riuscita, poiché %s non appartiene al gruppo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Devi fornire una password per creare un collegamento pubblico, sono consentiti solo i collegamenti protetti", "Sharing %s failed, because sharing with links is not allowed" : "Condivisione di %s non riuscita, poiché i collegamenti non sono consentiti", "Not allowed to create a federated share with the same user" : "Non è consentito creare una condivisione federata con lo stesso utente", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "La condivisione di %s non è riuscita, impossibile trovare %s, è probabile che il server non sia al momento raggiungibile.", "Share type %s is not valid for %s" : "Il tipo di condivisione %s non è valido per %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Impossibile impostare la data di scadenza. Le condivisioni non possono scadere più tardi di %s dalla loro attivazione", "Cannot set expiration date. Expiration date is in the past" : "Impossibile impostare la data di scadenza. La data di scadenza è nel passato.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Il motore di condivisione %s deve implementare l'interfaccia OCP\\Share_Backend", "Sharing backend %s not found" : "Motore di condivisione %s non trovato", "Sharing backend for %s not found" : "Motore di condivisione di %s non trovato", "Sharing failed, because the user %s is the original sharer" : "Condivisione non riuscita, poiché l'utente %s ha condiviso in origine", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Condivisione di %s non riuscita, poiché i permessi superano quelli accordati a %s", "Sharing %s failed, because resharing is not allowed" : "Condivisione di %s non riuscita, poiché la ri-condivisione non è consentita", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Condivisione di %s non riuscita, poiché il motore di condivisione per %s non riesce a trovare la sua fonte", "Sharing %s failed, because the file could not be found in the file cache" : "Condivisione di %s non riuscita, poiché il file non è stato trovato nella cache", "Can’t increase permissions of %s" : "Impossibile aumentare i permessi di %s", "Files can’t be shared with delete permissions" : "I file non possono essere condivisi con permessi di eliminazione", "Files can’t be shared with create permissions" : "I file non possono essere condivisi con permessi di creazione", "Expiration date is in the past" : "La data di scadenza è nel passato", "Can’t set expiration date more than %s days in the future" : "Impossibile impostare la data di scadenza a più di %s giorni nel futuro", "%s shared »%s« with you" : "%s ha condiviso «%s» con te", "%s shared »%s« with you." : "%s ha condiviso «%s» con te.", "Click the button below to open it." : "Fai clic sul pulsante sotto per aprirlo.", "Open »%s«" : "Apri «%s»", "%s via %s" : "%s tramite %s", "The requested share does not exist anymore" : "La condivisione richiesta non esiste più", "Could not find category \"%s\"" : "Impossibile trovare la categoria \"%s\"", "Sunday" : "Domenica", "Monday" : "Lunedì", "Tuesday" : "Martedì", "Wednesday" : "Mercoledì", "Thursday" : "Giovedì", "Friday" : "Venerdì", "Saturday" : "Sabato", "Sun." : "Dom.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mer.", "Thu." : "Gio.", "Fri." : "Ven.", "Sat." : "Sab.", "Su" : "Do", "Mo" : "Lu", "Tu" : "Ma", "We" : "Me", "Th" : "Gi", "Fr" : "Ve", "Sa" : "Sa", "January" : "Gennaio", "February" : "Febbraio", "March" : "Marzo", "April" : "Aprile", "May" : "Maggio", "June" : "Giugno", "July" : "Luglio", "August" : "Agosto", "September" : "Settembre", "October" : "Ottobre", "November" : "Novembre", "December" : "Dicembre", "Jan." : "Gen.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Mag.", "Jun." : "Giu.", "Jul." : "Lug.", "Aug." : "Ago.", "Sep." : "Set.", "Oct." : "Ott.", "Nov." : "Nov.", "Dec." : "Dic.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Solo i seguenti caratteri sono consentiti in un nome utente: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-'\"", "A valid username must be provided" : "Deve essere fornito un nome utente valido", "Username contains whitespace at the beginning or at the end" : "Il nome utente contiene spazi all'inizio o alla fine", "Username must not consist of dots only" : "Il nome utente non può consistere di soli punti", "A valid password must be provided" : "Deve essere fornita una password valida", "The username is already being used" : "Il nome utente è già utilizzato", "Could not create user" : "Impossibile creare l'utente", "User disabled" : "Utente disabilitato", "Login canceled by app" : "Accesso annullato dall'applicazione", "No app name specified" : "Il nome dell'applicazione non è specificato", "App '%s' could not be installed!" : "L'applicazione '%s' non può essere installata!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "L'applicazione \"%s\" non può essere installata poiché le seguenti dipendenze non sono soddisfatte: %s", "a safe home for all your data" : "un posto sicuro per tutti i tuoi dati", "File is currently busy, please try again later" : "Il file è attualmente occupato, riprova più tardi", "Can't read file" : "Impossibile leggere il file", "Application is not enabled" : "L'applicazione non è abilitata", "Authentication error" : "Errore di autenticazione", "Token expired. Please reload page." : "Token scaduto. Ricarica la pagina.", "Unknown user" : "Utente sconosciuto", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nessun driver di database (sqlite, mysql o postgresql) installato", "Cannot write into \"config\" directory" : "Impossibile scrivere nella cartella \"config\"", "Cannot write into \"apps\" directory" : "Impossibile scrivere nella cartella \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella delle applicazioni o disabilitando il negozio di applicazioni nel file di configurazione. Vedi %s", "Cannot create \"data\" directory" : "Impossibile creare la cartella \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella radice. Vedi %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "I permessi possono essere normalmente corretti fornendo al server web accesso in scrittura alla cartella radice. Vedi %s.", "Setting locale to %s failed" : "L'impostazione della localizzazione a %s non è riuscita", "Please install one of these locales on your system and restart your webserver." : "Installa una delle seguenti localizzazioni sul tuo sistema e riavvia il server web.", "Please ask your server administrator to install the module." : "Chiedi all'amministratore del tuo server di installare il modulo.", "PHP module %s not installed." : "Il modulo PHP %s non è installato.", "PHP setting \"%s\" is not set to \"%s\"." : "L'impostazione \"%s\" di PHP non è configurata a \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Per eseguire nuovamente Nextcloud, modificare questa impostazione nel file php.ini", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload è impostata a \"%s\" invece del valore atteso \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Per correggere questo problema, imposta <code>mbstring.func_overload</code> a <code>0</code> nel tuo php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "È richiesta almeno la versione 2.7.0 di libxml2. Quella attualmente installata è la %s.", "To fix this issue update your libxml2 version and restart your web server." : "Per risolvere questo problema, aggiorna la tua versione di libxml2 e riavvia il server web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Sembra che PHP sia configurato per rimuovere i blocchi di documentazione in linea. Ciò renderà inaccessibili diverse applicazioni principali.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Ciò è causato probabilmente da una cache/acceleratore come Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Sono stati installati moduli PHP, ma sono elencati ancora come mancanti?", "Please ask your server administrator to restart the web server." : "Chiedi all'amministratore di riavviare il server web.", "PostgreSQL >= 9 required" : "Richiesto PostgreSQL >= 9", "Please upgrade your database version" : "Aggiorna la versione del tuo database", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Modifica i permessi in 0770 in modo tale che la cartella non sia leggibile dagli altri utenti.", "Your data directory is readable by other users" : "La cartella dei dati è leggibile dagli altri utenti", "Your data directory must be an absolute path" : "La cartella dei dati deve essere un percorso assoluto", "Check the value of \"datadirectory\" in your configuration" : "Controlla il valore di \"datadirectory\" nella tua configurazione", "Your data directory is invalid" : "La cartella dei dati non è valida", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Assicurati che ci sia un file \".ocdata\" nella radice della cartella data.", "Could not obtain lock type %d on \"%s\"." : "Impossibile ottenere il blocco di tipo %d su \"%s\".", "Storage unauthorized. %s" : "Archiviazione non autorizzata. %s", "Storage incomplete configuration. %s" : "Configurazione dell'archiviazione incompleta.%s", "Storage connection error. %s" : "Errore di connessione all'archiviazione. %s", "Storage is temporarily not available" : "L'archiviazione è temporaneamente non disponibile", "Storage connection timeout. %s" : "Timeout di connessione all'archiviazione. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Ciò può essere normalmente corretto %sfornendo al server web accesso in scrittura alla cartella \"config\"%s", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Il modulo con id: %s non esiste. Abilitalo nelle impostazioni delle applicazioni o contatta il tuo amministratore.", "Server settings" : "Impostazioni server", "DB Error: \"%s\"" : "Errore DB: \"%s\"", "Offending command was: \"%s\"" : "Il comando non consentito era: \"%s\"", "You need to enter either an existing account or the administrator." : "È necessario inserire un account esistente o l'amministratore.", "Offending command was: \"%s\", name: %s, password: %s" : "Il comando non consentito era: \"%s\", nome: %s, password: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Impostazione permessi per %s non riuscita, poiché i permessi superano i permessi accordati a %s", "Setting permissions for %s failed, because the item was not found" : "Impostazione permessi per %s non riuscita, poiché l'elemento non è stato trovato", "Cannot clear expiration date. Shares are required to have an expiration date." : "Impossibile cancellare la data di scadenza. Le condivisioni devono avere una data di scadenza.", "Cannot increase permissions of %s" : "Impossibile aumentare i permessi di %s", "Files can't be shared with delete permissions" : "I file non possono essere condivisi con permessi di eliminazione", "Files can't be shared with create permissions" : "I file non possono essere condivisi con permessi di creazione", "Cannot set expiration date more than %s days in the future" : "Impossibile impostare la data di scadenza a più di %s giorni nel futuro", "Personal" : "Personale", "Admin" : "Admin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Ciò può essere normalmente corretto %sfornendo al server web accesso in scrittura alla cartella \"apps\"%s o disabilitando il negozio di applicazioni nel file di configurazione.", "Cannot create \"data\" directory (%s)" : "Impossibile creare la cartella \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Ciò può essere normalmente corretto <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">fornendo al server web accesso in scrittura alla cartella radice</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "I permessi possono essere normalmente corretti %sfornendo al server web accesso in scrittura alla cartella radice%s.", "Data directory (%s) is readable by other users" : "La cartella dei dati (%s) è leggibile dagli altri utenti", "Data directory (%s) must be an absolute path" : "La cartella dei dati (%s) deve essere un percorso assoluto", "Data directory (%s) is invalid" : "La cartella dei dati (%s) non è valida", "Please check that the data directory contains a file \".ocdata\" in its root." : "Verifica che la cartella dei dati contenga un file \".ocdata\" nella sua radice." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/es_MX.js 0000604 00000057242 15247130447 0006704 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "¡No se puede escribir en el directorio \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Esto generalmente se resuelve dándole al servidor web acceso para escribir en el directorio config. ", "See %s" : "Ver %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio config. Por favor ve %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Los archivos de la aplicación %$1s no fueron correctamente remplazados. Por favor asegúrarte de que la versión sea compatible con el servidor.", "Sample configuration detected" : "Se ha detectado la configuración de muestra", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Se ha detectado que la configuración de muestra ha sido copiada. Esto puede arruiniar tu instalacón y no está soportado. Por favor lee la documentación antes de hacer cambios en el archivo config.php", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s y %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s y %5$s", "Education Edition" : "Edición Educativa", "Enterprise bundle" : "Paquete empresarial", "Groupware bundle" : "Paquete de Groupware", "Social sharing bundle" : "Paquete para compartir en redes sociales", "PHP %s or higher is required." : "Se requiere de PHP %s o superior.", "PHP with a version lower than %s is required." : "PHP con una versión inferiror a la %s es requerido. ", "%sbit or higher PHP required." : "se requiere PHP para %sbit o superior.", "Following databases are supported: %s" : "Las siguientes bases de datos están soportadas: %s", "The command line tool %s could not be found" : "No fue posible encontar la herramienta de línea de comando %s", "The library %s is not available." : "La biblioteca %s no está disponible. ", "Library %s with a version higher than %s is required - available version %s." : "La biblitoteca %s con una versión superiror a la %s es requerida - versión disponible %s.", "Library %s with a version lower than %s is required - available version %s." : "Se requiere de la biblioteca %s con una versión inferiror a la %s - la versión %s está disponible. ", "Following platforms are supported: %s" : "Las siguientes plataformas están soportadas: %s", "Server version %s or higher is required." : "Se requiere la versión del servidor %s o superior. ", "Server version %s or lower is required." : "La versión del servidor %s o inferior es requerdia. ", "Unknown filetype" : "Tipo de archivo desconocido", "Invalid image" : "Imagen inválida", "Avatar image is not square" : "La imagen del avatar no es un cuadrado", "today" : "hoy", "yesterday" : "ayer", "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"], "last month" : "mes pasado", "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"], "last year" : "año pasado", "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"], "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"], "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"], "seconds ago" : "hace segundos", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ", "File name is a reserved word" : "Nombre de archivo es una palabra reservada", "File name contains at least one invalid character" : "El nombre del archivo contiene al menos un caracter inválido", "File name is too long" : "El nombre del archivo es demasiado largo", "Dot files are not allowed" : "Los archivos Dot no están permitidos", "Empty filename is not allowed" : "El uso de nombres de archivo vacíos no está permitido", "App \"%s\" cannot be installed because appinfo file cannot be read." : "La aplicación \"%s\" no puede ser instalada porque el archivo appinfo no se puede leer. ", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "La aplicación \"%s\" no puede ser instalada porque no es compatible con esta versión del servidor. ", "This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, por favor no lo contestes. ", "Help" : "Ayuda", "Apps" : "Aplicaciones", "Settings" : "Configuraciones", "Log out" : "Salir", "Users" : "Usuarios", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Configuraciones básicas", "Sharing" : "Compartiendo", "Security" : "Seguridad", "Encryption" : "Encripción", "Additional settings" : "Configuraciones adicionales", "Tips & tricks" : "Consejos & trucos", "Personal info" : "Información personal", "Sync clients" : "Sincronizar clientes", "Unlimited" : "Ilimitado", "__language_name__" : "Español (México)", "Verifying" : "Verficando", "Verifying …" : "Verficando ...", "Verify" : "Verificar", "%s enter the database username and name." : "%s ingresa el usuario y nombre de la base de datos", "%s enter the database username." : "%s ingresa el nombre de usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s no puedes utilizar puntos en el nombre de la base de datos", "Oracle connection could not be established" : "No fue posible establecer la conexión a Oracle", "Oracle username and/or password not valid" : "Usuario y/o contraseña de Oracle inválidos", "PostgreSQL username and/or password not valid" : "El Usuario y/o Contraseña de PostgreSQL inválido(s)", "You need to enter details of an existing account." : "Necesitas ingresar los detalles de una cuenta existente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "OS X de Mac no está soportado y %s no funcionará correctamente en esta plataforma ¡Úsalo bajo tu propio riesgo!", "For the best results, please consider using a GNU/Linux server instead." : "Para mejores resultados, por favor cosidera usar en su lugar un servidor GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Al parecer esta instancia %s está corriendo en un ambiente PHP de 32-bits y el open_basedir ha sido configurado en el archivo php.ini. Esto generará problemas con archivos de más de 4GB de tamaño y es altamente desalentado. ", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor elimina el ajuste open_basedir de tu archivo php.ini o cambia a PHP de 64 bits. ", "Set an admin username." : "Establecer un Usuario administrador", "Set an admin password." : "Establecer la contraseña del administrador.", "Can't create or write into the data directory %s" : "No es posible crear o escribir en el directorio de datos %s", "Invalid Federated Cloud ID" : "ID Inválido", "Sharing %s failed, because the backend does not allow shares from type %i" : "Se presentó una falla al compartir %s, porque el backend no permite elementos compartidos de tipo %i", "Sharing %s failed, because the file does not exist" : "Se presentó una falla al compartir %s porque el archivo no existe", "You are not allowed to share %s" : "No tienes permitido compartir %s", "Sharing %s failed, because you can not share with yourself" : "Se presentó una falla al compartir %s, porque no puedes compartir contigo mismo", "Sharing %s failed, because the user %s does not exist" : "Se presentó una falla al compartir %s porque el usuario %s no existe", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Se presentó una falla al compartir %s porque el usuario %s no es un miembro de ninguno de los grupos de los cuales %s es miembro", "Sharing %s failed, because this item is already shared with %s" : "Se presentó una falla al compartir %s, porque este elemento ya había sido compartido con %s", "Sharing %s failed, because this item is already shared with user %s" : "Se presento una falla al compartir %s, porque este elemento ya ha sido compartido con el usuario %s", "Sharing %s failed, because the group %s does not exist" : "Se presentó una falla al compartir %s, porque el grupo %s no existe", "Sharing %s failed, because %s is not a member of the group %s" : "Se presentó una falla al compartir %s debido a que %s no es un miembro del grupo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Necesitas proporcionar una contraseña para crear una liga pública, sólo se permiten ligas protegidas. ", "Sharing %s failed, because sharing with links is not allowed" : "Se presentó una falla al compartir %s porque no está permitido compartir con ligas", "Not allowed to create a federated share with the same user" : "No está permitido crear un elemento compartido con el mismo usuario", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Se presentó una falla al compartir %s, no fue posible encontrar %s, tal vez el servidor sea inalcanzable por el momento", "Share type %s is not valid for %s" : "El tipo del elemento compartido %s no es válido para %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "No ha sido posible establecer la fecha de expiración. Los recursos compartidos no pueden expirar después de %s tras haber sido compartidos", "Cannot set expiration date. Expiration date is in the past" : "No ha sido posible establecer la fecha de expiración. La fecha de expiración ya ha pasado", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El backend %s que comparte debe implementar la interface OCP\\Share_Backend", "Sharing backend %s not found" : "No fue encontrado el Backend que comparte %s ", "Sharing backend for %s not found" : "No fue encontrado el Backend que comparte para %s", "Sharing failed, because the user %s is the original sharer" : "Se presentó una falla al compartir, porque el usuario %s es quien compartió originalmente", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Se presentó una falla al compartir %s, porque los permisos exceden los permisos otorgados a %s", "Sharing %s failed, because resharing is not allowed" : "Falla al compartir %s debído a que no se permite volver a compartir", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Se presentó una falla al compartir %s porque el backend que comparte %s no pudo encontrar su origen", "Sharing %s failed, because the file could not be found in the file cache" : "Se presentó una falla al compartir %s porque el archivo no se encontró en el caché de archivos", "Can’t increase permissions of %s" : "No es posible incrementar los privilegios de %s", "Files can’t be shared with delete permissions" : "Los archivos no se pueden compartir con permisos de borrado", "Files can’t be shared with create permissions" : "Los archivos no se pueden compartir con permisos de creación", "Expiration date is in the past" : "La fecha de expiración se encuentra en el pasado", "Can’t set expiration date more than %s days in the future" : "No es posible establecer la fecha de expiración más allá de %s días en el futuro", "%s shared »%s« with you" : "%s ha compartido »%s« contigo", "%s shared »%s« with you." : "%s compartió contigo »%s«.", "Click the button below to open it." : "Haz click en el botón inferior para abrirlo. ", "Open »%s«" : "Abrir »%s«", "%s via %s" : "%s por %s", "The requested share does not exist anymore" : "El recurso compartido solicitado ya no existe", "Could not find category \"%s\"" : "No fue posible encontrar la categoria \"%s\"", "Sunday" : "Domingo", "Monday" : "Lunes", "Tuesday" : "Martes", "Wednesday" : "Miércoles", "Thursday" : "Jueves", "Friday" : "Viernes", "Saturday" : "Sábado", "Sun." : "Dom.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mie.", "Thu." : "Jue.", "Fri." : "Vie.", "Sat." : "Sab.", "Su" : "Do", "Mo" : "Lu", "Tu" : "Ma", "We" : "Mi", "Th" : "Ju", "Fr" : "Vi", "Sa" : "Sa", "January" : "Enero", "February" : "Febrero", "March" : "Marzo", "April" : "Abril", "May" : "Mayo", "June" : "Junio", "July" : "Julio", "August" : "Agosto", "September" : "Septiembre", "October" : "Octubre", "November" : "Noviembre", "December" : "Diciembre", "Jan." : "Ene.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "May.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Ago.", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Dic.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Sólo se permiten los siguientes caracteres en el usuario: \"a-z\", \"A-Z\", \"0-9\" y \"_.@-'\"", "A valid username must be provided" : "Debes proporcionar un nombre de usuario válido", "Username contains whitespace at the beginning or at the end" : "El usuario contiene un espacio en blanco al inicio o al final", "Username must not consist of dots only" : "El usuario no debe consistir de solo puntos. ", "A valid password must be provided" : "Se debe proporcionar una contraseña válida", "The username is already being used" : "Ese usuario ya está en uso", "Could not create user" : "No fue posible crear el usuario", "User disabled" : "Usuario deshabilitado", "Login canceled by app" : "Inicio de sesión cancelado por la aplicación", "No app name specified" : "No se ha especificado el nombre de la aplicación", "App '%s' could not be installed!" : "¡La aplicación \"%s\" no pudo ser instalada!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "La aplicación \"%s\" no puede ser instalada porque las siguientes dependencias no están satisfechas: %s ", "a safe home for all your data" : "un lugar seguro para todos tus datos", "File is currently busy, please try again later" : "El archivo se encuentra actualmente en uso, por favor intentalo más tarde. ", "Can't read file" : "No se puede leer el archivo", "Application is not enabled" : "La aplicación está deshabilitada", "Authentication error" : "Error de autenticación", "Token expired. Please reload page." : "La ficha ha expirado. Por favor recarga la página.", "Unknown user" : "Ususario desconocido", "No database drivers (sqlite, mysql, or postgresql) installed." : "No cuentas con controladores de base de datos (sqlite, mysql o postgresql) instalados. ", "Cannot write into \"config\" directory" : "No fue posible escribir en el directorio \"config\"", "Cannot write into \"apps\" directory" : "No fue posible escribir en el directorio \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio de las aplicaciones o deshabilitando la appstore en el archivo config. Por favor ve %s", "Cannot create \"data\" directory" : "No fue posible crear el directorio \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio raíz. Por favor ve %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Por lo general los permisos se pueden corregir al darle al servidor web acceso de escritura al directorio raíz. Por favor ve %s.", "Setting locale to %s failed" : "Se presentó una falla al establecer la regionalización a %s", "Please install one of these locales on your system and restart your webserver." : "Por favor instala uno de las siguientes configuraciones locales en tu sistema y reinicia tu servidor web", "Please ask your server administrator to install the module." : "Por favor solicita a tu adminsitrador la instalación del módulo. ", "PHP module %s not installed." : "El módulo de PHP %s no está instalado. ", "PHP setting \"%s\" is not set to \"%s\"." : "El ajuste PHP \"%s\" no esta establecido a \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "El cambiar este ajuste del archivo php.ini hará que Nextcloud corra de nuevo.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload está establecido como \"%s\" en lugar del valor esperado de \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Para corregir este tema, establece <code>mbstring.func_overload</code> a <code>0</code> en tu archivo php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Se requiere de por lo menos libxml2 2.7.0. Actualmente %s está instalado. ", "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este tema, por favor actualiza la versión de su libxml2 y reinicia tu servidor web. ", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Al parecer PHP está configurado para quitar los bloques de comentarios internos. Esto hará que varias aplicaciones principales sean inaccesibles. ", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Esto ha sido causado probablemente por un acelerador de caché como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "¿Los módulos de PHP han sido instalados, pero se siguen enlistando como faltantes?", "Please ask your server administrator to restart the web server." : "Por favor solicita al administrador reiniciar el servidor web. ", "PostgreSQL >= 9 required" : "Se requiere PostgreSQL >= 9", "Please upgrade your database version" : "Por favor actualiza tu versión de la base de datos", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor cambia los permisos a 0770 para que el directorio no pueda ser enlistado por otros usuarios. ", "Your data directory is readable by other users" : "Tu direcctorio data puede ser leído por otros usuarios", "Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta", "Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración", "Your data directory is invalid" : "Tu directorio de datos es inválido", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Asegurate de que exista una archivo llamado \".ocdata\" en la raíz del directorio de datos. ", "Could not obtain lock type %d on \"%s\"." : "No fue posible obtener el tipo de bloqueo %d en \"%s\". ", "Storage unauthorized. %s" : "Almacenamiento no autorizado. %s", "Storage incomplete configuration. %s" : "Configuración incompleta del almacenamiento. %s", "Storage connection error. %s" : "Se presentó un error con la conexión al almacenamiento. %s", "Storage is temporarily not available" : "El almacenamieto se encuentra temporalmente no disponible", "Storage connection timeout. %s" : "El tiempo de la conexión del almacenamiento se agotó. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Esto generalmente se soluciona %s dándole al servidor web acceso para escribir en el directorio config %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con id: %s no existe. Por favor habilítalo en tus configuraciones de aplicación o contacta a tu administrador. ", "Server settings" : "Configuraciones del servidor", "DB Error: \"%s\"" : "Error de BD: \"%s\"", "Offending command was: \"%s\"" : "El comando infractor fue: \"%s\"", "You need to enter either an existing account or the administrator." : "Necesitas ingresar una cuenta ya existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "El comando infractor fue: \"%s\", nombre: %s, contraseña: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Se presentó una falla al establecer los permisos para %s, porque los permisos exceden los permisos otorgados a %s", "Setting permissions for %s failed, because the item was not found" : "Se persentó una falla al establecer los permisos para %s, porque no se encontró el elemento ", "Cannot clear expiration date. Shares are required to have an expiration date." : "No ha sido posible borrar la fecha de expiración. Los elelentos compartidos deben tener una fecha de expiración.", "Cannot increase permissions of %s" : "No se pueden incrementar los permisos de %s", "Files can't be shared with delete permissions" : "No es posible compartir archivos con permisos de borrado", "Files can't be shared with create permissions" : "No es posible compartir archivos con permisos de creación", "Cannot set expiration date more than %s days in the future" : "No es posible establecer la fecha de expiración más allá de %s días en el futuro", "Personal" : "Personal", "Admin" : "Administración", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Esto se puede arreglar por %s al darle acceso de escritura al servidor web al directorio de las aplicaciones %s o al deshabilitar la tienda de aplicaciones en el archivo de configuración", "Cannot create \"data\" directory (%s)" : "No fue posible crear el directorio de \"datos\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Esto se puede arreglar generalmente al <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">darle al servidor web accesos al directorio raíz</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Los permisos se pueden arreglar generalmente al %s darle al servidor web accesos al direcotiro raíz %s.", "Data directory (%s) is readable by other users" : "El directorio de datos (%s) puede ser leído por otros usuarios", "Data directory (%s) must be an absolute path" : "El directorio de datos (%s) debe ser una ruta absoluta", "Data directory (%s) is invalid" : "El directorio de datos (%s) es inválido", "Please check that the data directory contains a file \".ocdata\" in its root." : "Por favor verifica que el directorio de datos tenga un archivo \".ocdata\" en su raíz. " }, "nplurals=2; plural=(n != 1);"); l10n/hu.js 0000604 00000050343 15247130447 0006300 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Nem írható a \"config\" könyvtár!", "This can usually be fixed by giving the webserver write access to the config directory" : "Ez rendszerint úgy oldható meg, hogy írási jogot adunk a webszervernek a config könyvtárra.", "See %s" : "Lásd %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ez rendszerint úgy oldható meg, hogy írási jogot adunk a webszervernek a config könyvtárra. Lásd: %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "%$1s alkalmazás fájljai nem megfelelően lettek cserélve. Győződj meg róla, hogy ez a verzió kompatibilis-e a szerverrel.", "Sample configuration detected" : "A példabeállítások vannak beállítva", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Úgy tűnik a példakonfigurációt próbálja ténylegesen használni. Ez nem támogatott, és működésképtelenné teheti a telepítést. Kérlek olvasd el a dokumentációt és azt követően változtas a config.php-n!", "%1$s and %2$s" : "%1$s és %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s és %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s és %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s és %5$s", "Education Edition" : "Oktatási verzió", "Enterprise bundle" : "Vállalati csomag", "Groupware bundle" : "Csoportmunka csomag", "Social sharing bundle" : "Közösségi megosztás csomag", "PHP %s or higher is required." : "PHP %s vagy ennél újabb szükséges.", "PHP with a version lower than %s is required." : "Ennél régebbi PHP szükséges: %s.", "%sbit or higher PHP required." : "%sbites vagy újabb PHP szükséges.", "Following databases are supported: %s" : "A következő adatbázisok támogatottak: %s", "The command line tool %s could not be found" : "A parancssori eszköz nem található: %s", "The library %s is not available." : "A könyvtár %s nem áll rendelkezésre.", "Library %s with a version higher than %s is required - available version %s." : "%s könyvtár %s vagy újabb verziója szükséges - elérhető verzió: %s.", "Library %s with a version lower than %s is required - available version %s." : "%s könyvtár %s vagy régebbi verziója szükséges - elérhető verzió: %s.", "Following platforms are supported: %s" : "Ezek a platformok támogatottak: %s", "Server version %s or higher is required." : "%s vagy újabb szerver verzió szükséges.", "Server version %s or lower is required." : "%s vagy régebbi szerver verzió szükséges.", "Unknown filetype" : "Ismeretlen fájl típus", "Invalid image" : "Hibás kép", "Avatar image is not square" : "Az avatár kép nem négyzetes.", "today" : "ma", "yesterday" : "tegnap", "_%n day ago_::_%n days ago_" : ["%n napja","%n napja"], "last month" : "múlt hónapban", "_%n month ago_::_%n months ago_" : ["%n hónapja","%n hónapja"], "last year" : "tavaly", "_%n year ago_::_%n years ago_" : ["%n éve","%n éve"], "_%n hour ago_::_%n hours ago_" : ["%n órája","%n órája"], "_%n minute ago_::_%n minutes ago_" : ["%n perce","%n perce"], "seconds ago" : "pár másodperce", "File name is a reserved word" : "A fajl neve egy rezervált szó", "File name contains at least one invalid character" : "A fájlnév legalább egy érvénytelen karaktert tartalmaz!", "File name is too long" : "A fájlnév túl hosszú!", "Dot files are not allowed" : "Pontozott fájlok nem engedétlyezettek", "Empty filename is not allowed" : "Üres fájlnév nem engedétlyezett", "App \"%s\" cannot be installed because appinfo file cannot be read." : "\"%s\" alkalmazás nem lehet telepíteni, mert az appinfo fájl nem olvasható.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "\"%s\" alkalmazás nem lehet telepíteni, mert nem kompatibilis a szerver jelen verziójával.", "Help" : "Súgó", "Apps" : "Alkalmazások", "Settings" : "Beállítások", "Log out" : "Kijelentkezés", "Users" : "Felhasználók", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Alapvető beállítások", "Sharing" : "Megosztás", "Security" : "Biztonság", "Encryption" : "Titkosítás", "Additional settings" : "További beállítások", "Tips & tricks" : "Tippek és trükkök", "Verifying …" : "Ellenőrzés...", "Verify" : "Ellenőrzés", "%s enter the database username and name." : "%s add meg az adatbázis nevét és felhasználónevét", "%s enter the database username." : "%s adja meg az adatbázist elérő felhasználó login nevét.", "%s enter the database name." : "%s adja meg az adatbázis nevét.", "%s you may not use dots in the database name" : "%s az adatbázis neve nem tartalmazhat pontot", "Oracle connection could not be established" : "Az Oracle kapcsolat nem hozható létre", "Oracle username and/or password not valid" : "Az Oracle felhasználói név és/vagy jelszó érvénytelen", "PostgreSQL username and/or password not valid" : "A PostgreSQL felhasználói név és/vagy jelszó érvénytelen", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "A Mac OS X nem támogatott és %s nem lesz teljesen működőképes. Csak saját felelősségre használja!", "For the best results, please consider using a GNU/Linux server instead." : "A legjobb eredmény érdekében érdemes GNU/Linux-alapú szervert használni.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Úgy tűnik, hogy ez a %s példány 32-bites PHP környezetben fut és az open_basedir konfigurálva van a php.ini fájlban. Ez 4 GB-nál nagyobb fájlok esetén problémákat okozhat így erősen ellenjavallt.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Kérlek távolítsd el az open_basedir beállítást a php.ini-ből, vagy válts 64bit-es PHP-ra.", "Set an admin username." : "Állítson be egy felhasználói nevet az adminisztrációhoz.", "Set an admin password." : "Állítson be egy jelszót az adminisztrációhoz.", "Can't create or write into the data directory %s" : "Nem sikerült létrehozni vagy irni a \"data\" könyvtárba %s", "Invalid Federated Cloud ID" : "Érvénytelen Egyesített Felhő Azonosító", "Sharing %s failed, because the backend does not allow shares from type %i" : "%s megosztása sikertelen, mert a megosztási alrendszer nem engedi a %l típus megosztását", "Sharing %s failed, because the file does not exist" : "%s megosztása sikertelen, mert a fájl nem létezik", "You are not allowed to share %s" : "Nincs jogosultságod %s megosztására", "Sharing %s failed, because you can not share with yourself" : "%s megosztása sikertelen, mert magaddal nem oszthatod meg", "Sharing %s failed, because the user %s does not exist" : "%s megosztása nem sikerült, mert %s felhasználó nem létezik", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s megosztása nem sikerült, mert %s felhasználó nem tagja egyik olyan csoportnak sem, aminek %s tagja", "Sharing %s failed, because this item is already shared with %s" : "%s megosztása nem sikerült, mert ez már meg van osztva %s-vel", "Sharing %s failed, because this item is already shared with user %s" : "%s megosztása sikertelen, mert már meg van osztva %s felhasználóval", "Sharing %s failed, because the group %s does not exist" : "%s megosztása nem sikerült, mert %s csoport nem létezik", "Sharing %s failed, because %s is not a member of the group %s" : "%s megosztása nem sikerült, mert %s felhasználó nem tagja a %s csoportnak", "You need to provide a password to create a public link, only protected links are allowed" : "Meg kell adnia egy jelszót is, mert a nyilvános hivatkozások csak jelszóval védetten használhatók", "Sharing %s failed, because sharing with links is not allowed" : "%s megosztása nem sikerült, mert a hivatkozással történő megosztás nincs engedélyezve", "Not allowed to create a federated share with the same user" : "Azonos felhasználóval nem lehet létrehozni egyesített megosztást.", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s megosztása sikertelen, mert %s nem található, talán a szerver jelenleg nem elérhető.", "Share type %s is not valid for %s" : "A %s megosztási típus nem érvényes %s-re", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Nem lehet beállítani a lejárati időt. A megosztások legfeljebb ennyi idővel járhatnak le a létrehozásukat követően: %s", "Cannot set expiration date. Expiration date is in the past" : "Nem lehet beállítani a lejárati időt, mivel a megadott lejárati időpont már elmúlt.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Az %s megosztási alrendszernek támogatnia kell az OCP\\Share_Backend interface-t", "Sharing backend %s not found" : "A %s megosztási alrendszer nem található", "Sharing backend for %s not found" : "%s megosztási alrendszere nem található", "Sharing failed, because the user %s is the original sharer" : "Megosztás sikertelen, mert %s felhasználó az eredeti megosztó", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "%s megosztása nem sikerült, mert a jogosultságok túllépik azt, ami %s rendelkezésére áll", "Sharing %s failed, because resharing is not allowed" : "%s megosztása nem sikerült, mert a megosztás továbbadása nincs engedélyezve", "Sharing %s failed, because the sharing backend for %s could not find its source" : "%s megosztása nem sikerült, mert %s megosztási alrendszere nem találja", "Sharing %s failed, because the file could not be found in the file cache" : "%s megosztása nem sikerült, mert a fájl nem található a gyorsítótárban", "Expiration date is in the past" : "A lejárati dátum már elmúlt", "%s shared »%s« with you" : "%s megosztotta veled ezt: »%s«", "%s via %s" : "%s - %s", "Could not find category \"%s\"" : "Ez a kategória nem található: \"%s\"", "Sunday" : "Vasárnap", "Monday" : "Hétfő", "Tuesday" : "Kedd", "Wednesday" : "Szerda", "Thursday" : "Csütörtök", "Friday" : "Péntek", "Saturday" : "Szombat", "Sun." : "Vas.", "Mon." : "Hé.", "Tue." : "Ke.", "Wed." : "Sze.", "Thu." : "Csü.", "Fri." : "Pén.", "Sat." : "Szo.", "Su" : "Va", "Mo" : "Hé", "Tu" : "Ke", "We" : "Sze", "Th" : "Cs", "Fr" : "Pé", "Sa" : "Szo", "January" : "Január", "February" : "Február", "March" : "Március", "April" : "Április", "May" : "Május", "June" : "Június", "July" : "Július", "August" : "Augusztus", "September" : "Szeptember", "October" : "Október", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Már.", "Apr." : "Ápr.", "May." : "Máj.", "Jun." : "Jún.", "Jul." : "Júl.", "Aug." : "Aug.", "Sep." : "Szep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "A felhasználónévben csak a következő karakterek engedélyezettek: \"a-z\", \"A-Z\", \"0-9\", és \"_.@-'\"", "A valid username must be provided" : "Érvényes felhasználónevet kell megadnia", "Username contains whitespace at the beginning or at the end" : "A felhasználónév szóközt tartalmaz az elején vagy a végén", "A valid password must be provided" : "Érvényes jelszót kell megadnia", "The username is already being used" : "Ez a bejelentkezési név már foglalt", "User disabled" : "Felhasználó letiltva", "Login canceled by app" : "Bejelentkezés megszakítva az alkalmazás által", "No app name specified" : "Nincs az alkalmazás név megadva.", "App '%s' could not be installed!" : "\"%s\" alkalmazás nem lehet telepíthető!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "\"%s\" alkalmazás nem lehet telepíteni, mert a következő függőségek nincsenek kielégítve: %s", "a safe home for all your data" : "egy biztonságos hely az adataidnak", "File is currently busy, please try again later" : "A fájl jelenleg elfoglalt, kérjük próbáld újra később!", "Can't read file" : "Nem olvasható a fájl", "Application is not enabled" : "Az alkalmazás nincs engedélyezve", "Authentication error" : "Azonosítási hiba", "Token expired. Please reload page." : "A token lejárt. Frissítse az oldalt.", "Unknown user" : "Ismeretlen felhasználó", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nincs telepítve adatbázis-meghajtóprogram (sqlite, mysql vagy postgresql).", "Cannot write into \"config\" directory" : "Nem írható a \"config\" könyvtár", "Cannot write into \"apps\" directory" : "Nem írható az \"apps\" könyvtár", "Setting locale to %s failed" : "A lokalizáció %s-re való állítása nem sikerült", "Please install one of these locales on your system and restart your webserver." : "Kérjük állítsa be a következő lokalizációk valamelyikét a rendszeren és indítsa újra a webszervert!", "Please ask your server administrator to install the module." : "Kérje meg a rendszergazdát, hogy telepítse a modult!", "PHP module %s not installed." : "A %s PHP modul nincs telepítve.", "PHP setting \"%s\" is not set to \"%s\"." : "%s PHP beállítás nincs \"%s\"-re állítva.", "Adjusting this setting in php.ini will make Nextcloud run again" : "A beállítás változtatása a php.ini fájlban újra futtatja a Nexcloud-ot", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload értéke: \"%s\" az elvárt \"0\" helyett", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "A probléma javításához állítsd a <code>mbstring.func_overload</code> értékét <code>0</code>-ra a php.ini fájlban.", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Legalább libxml2 2.7.0 szükséges. Jelenleg telepített: %s", "To fix this issue update your libxml2 version and restart your web server." : "A probléma javításához frissítsd a libxml2 verziót és indítsd újra a webszervert.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Úgy tűnik, hogy a PHP úgy van beállítva, hogy eltávolítja programok belsejében elhelyezett szövegblokkokat. Emiatt a rendszer több alapvető fontosságú eleme működésképtelen lesz.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Ezt valószínűleg egy gyorsítótár ill. kódgyorsító, mint pl, a Zend, OPcache vagy eAccelererator okozza.", "PHP modules have been installed, but they are still listed as missing?" : "A PHP modulok telepítve vannak, de a listában mégsincsenek felsorolva?", "Please ask your server administrator to restart the web server." : "Kérje meg a rendszergazdát, hogy indítsa újra a webszervert!", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 szükséges", "Please upgrade your database version" : "Kérem frissítse az adatbázis-szoftvert!", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Kérjük módosítsa a könyvtár elérhetőségi engedélybeállítását 0770-re, hogy a tartalmát más felhasználó ne listázhassa!", "Check the value of \"datadirectory\" in your configuration" : "Ellenőrizd a \"datadirectory\" értékét a konfigurációban", "Could not obtain lock type %d on \"%s\"." : "Nem sikerült %d típusú zárolást elérni itt: \"%s\".", "Storage unauthorized. %s" : "A tároló jogosulatlan. %s", "Storage incomplete configuration. %s" : "A tároló beállítása nem teljes. %s", "Storage connection error. %s" : "Tároló kapcsolódási hiba. %s", "Storage is temporarily not available" : "A tároló átmenetileg nem érthető el", "Storage connection timeout. %s" : "Tároló kapcsolat időtúllépés. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Ez rendszerint úgy oldható meg, hogy %sírási jogot adunk a webszervernek a config könyvtárra%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "A modul nem létezik, id: %s. Kérlek engedélyezd az alkalmazás beállításoknál vagy keresd az adminisztrátort.", "Server settings" : "Szerver beállítások", "DB Error: \"%s\"" : "Adatbázis hiba: \"%s\"", "Offending command was: \"%s\"" : "A hibát ez a parancs okozta: \"%s\"", "You need to enter either an existing account or the administrator." : "Vagy egy létező felhasználó vagy az adminisztrátor bejelentkezési nevét kell megadnia", "Offending command was: \"%s\", name: %s, password: %s" : "A hibát okozó parancs ez volt: \"%s\", login név: %s, jelszó: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Nem sikerült %s-re beállítani az elérési jogosultságokat, mert a megadottak túllépik a %s-re érvényes jogosultságokat", "Setting permissions for %s failed, because the item was not found" : "Nem sikerült %s-re beállítani az elérési jogosultságokat, mert a kérdéses fájl nem található", "Cannot clear expiration date. Shares are required to have an expiration date." : "Nem lehet beállítani a lejárati időt. A megosztásoknak kötelező megadni lejárati időt!", "Cannot increase permissions of %s" : "%s jogosultságait nem lehet megemelni", "Files can't be shared with delete permissions" : "A fájlokat nem lehet megosztani a törlési jogosultságokkal", "Files can't be shared with create permissions" : "A fájlokat nem lehet megosztani a létrehozási jogosultságokkal", "Cannot set expiration date more than %s days in the future" : "%s napnál távolabbi lejárati dátumot nem lehet beállítani", "Personal" : "Személyes", "Admin" : "Adminisztrátor", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Ez rendszerint úgy oldható meg, hogy %sírási jogot adunk a webszervernek az app könyvtárra%s, vagy letiltjuk a config fájlban az appstore használatát.", "Cannot create \"data\" directory (%s)" : "Nem sikerült létrehozni a \"data\" könyvtárt (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Ez általában úgy javítható, hogy <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">a webszervernek írási jogosultságot adsz a root könyvtárra</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Az elérési problémák rendszerint megoldhatók azzal, ha a %swebszervernek írásjogot adunk a gyökérkönyvtárra%s.", "Data directory (%s) is readable by other users" : "Az adatkönyvtár (%s) más felhasználók számára is olvasható ", "Data directory (%s) must be an absolute path" : "Az adatkönyvtárnak (%s) abszolút elérési útnak kell lennie", "Data directory (%s) is invalid" : "Érvénytelen a megadott adatkönyvtár (%s) ", "Please check that the data directory contains a file \".ocdata\" in its root." : "Kérjük ellenőrizze, hogy az adatkönyvtár tartalmaz a gyökerében egy \".ocdata\" nevű fájlt!" }, "nplurals=2; plural=(n != 1);"); l10n/sv.js 0000604 00000054222 15247130447 0006314 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Kan inte skriva till \"config\" katalogen!", "This can usually be fixed by giving the webserver write access to the config directory" : "Detta kan vanligtvis åtgärdas genom att ge skrivrättigheter till config-katalogen", "See %s" : "Se %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Detta fixas vanligtvis genom att ge webbservern skrivrättigheter till konfigureringsmappen. Se %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Filerna i appen %$1s ersattes inte korrekt. Se till att det är en version som är kompatibel med servern.", "Sample configuration detected" : "Exempel-konfiguration detekterad", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Det har detekterats att exempel-konfigurationen har kopierats. Detta kan förstöra din installation och stöds ej. Vänligen läs dokumentationen innan ändringar på config.php utförs", "%1$s and %2$s" : "%1$s och %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s och %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s och %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s och %5$s", "Education Edition" : "Utbildningsversion", "Enterprise bundle" : "Företagspaket", "Groupware bundle" : "Groupware-paket", "Social sharing bundle" : "Social delnings-paket", "PHP %s or higher is required." : "PHP %s eller högre krävs.", "PHP with a version lower than %s is required." : "PHP med version lägre än %s krävs.", "%sbit or higher PHP required." : "%sbit eller nyare PHP-version krävs.", "Following databases are supported: %s" : "Följande databastyper stöds: %s", "The command line tool %s could not be found" : "Kommandoradsverktyget %s hittades inte.", "The library %s is not available." : "Biblioteket %s är inte tillgängligt.", "Library %s with a version higher than %s is required - available version %s." : "Bibliotek %s med version högre än %s krävs - tillgänglig version %s.", "Library %s with a version lower than %s is required - available version %s." : "Bibliotek %s med version lägre än %s krävs - tillgänglig version %s.", "Following platforms are supported: %s" : "Följande plattformar stöds: %s", "Server version %s or higher is required." : "Serverversion %s eller nyare krävs.", "Server version %s or lower is required." : "Serverversion %s eller äldre krävs.", "Unknown filetype" : "Okänd filtyp", "Invalid image" : "Ogiltig bild", "Avatar image is not square" : "Profilbilden är inte fyrkantig", "today" : "i dag", "yesterday" : "i går", "_%n day ago_::_%n days ago_" : ["%n dag sedan","%n dagar sedan"], "last month" : "förra månaden", "_%n month ago_::_%n months ago_" : ["%n månad sedan","%n månader sedan"], "last year" : "förra året", "_%n year ago_::_%n years ago_" : ["%n år sedan","%n år sedan"], "_%n hour ago_::_%n hours ago_" : ["%n timme sedan","%n timmar sedan"], "_%n minute ago_::_%n minutes ago_" : ["%n minut sedan","%n minuter sedan"], "seconds ago" : "sekunder sedan", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finns inte längre. Vänligen aktivera det i dina appinställningar eller kontakta din administratör.", "File name is a reserved word" : "Filnamnet är ett reserverat ord", "File name contains at least one invalid character" : "Filnamnet innehåller minst ett ogiltigt tecken", "File name is too long" : "Filnamnet är för långt", "Dot files are not allowed" : "Dot-filer är inte tillåtna", "Empty filename is not allowed" : "Tomma filnamn är inte tillåtna", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Applikationen \"%s\" kan ej installeras eftersom informationen från appen ej kunde läsas.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Applikationen \"%s\" kan ej installeras eftersom den inte är kompatibel med denna serverversion.", "This is an automatically sent email, please do not reply." : "Detta är ett automatiskt skickat e-postmeddelande, svara inte på detta mejl.", "Help" : "Hjälp", "Apps" : "Applikationer", "Settings" : "Inställningar", "Log out" : "Logga ut", "Users" : "Användare", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Vanliga inställningar", "Sharing" : "Delning", "Security" : "Säkerhet", "Encryption" : "Kryptering", "Additional settings" : "Övriga inställningar", "Tips & tricks" : "Tips & tricks", "Personal info" : "Personlig information", "Sync clients" : "Synkklienter", "Unlimited" : "Obegränsad", "__language_name__" : "__language_name__", "Verifying" : "Verifierar", "Verifying …" : "Verifierar ...", "Verify" : "Verifiera", "%s enter the database username and name." : "%s ange användarnamn och namn för databasen.", "%s enter the database username." : "%s ange databasanvändare.", "%s enter the database name." : "%s ange databasnamn", "%s you may not use dots in the database name" : "%s du får inte använda punkter i databasnamnet", "Oracle connection could not be established" : "Oracle-anslutning kunde inte etableras", "Oracle username and/or password not valid" : "Oracle-användarnamnet och/eller lösenordet är felaktigt", "PostgreSQL username and/or password not valid" : "PostgreSQL-användarnamnet och/eller lösenordet är felaktigt", "You need to enter details of an existing account." : "Du måste ange inloggningsuppgifter av ett aktuellt konto.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X stöds inte och %s kommer inte att fungera korrekt på denna plattform. Använd på egen risk!", "For the best results, please consider using a GNU/Linux server instead." : "För bästa resultat, överväg att använda en GNU/Linux server istället.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Det verkar som om denna %s instans körs på en 32-bitars PHP miljö och open_basedir har konfigurerats i php.ini. Detta kommer att leda till problem med filer över 4 GB och är verkligen inte rekommenderat!", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Ta bort open_basedir i din php.ini eller byt till 64-bitars PHP.", "Set an admin username." : "Ange ett användarnamn för administratören.", "Set an admin password." : "Ange ett administratörslösenord.", "Can't create or write into the data directory %s" : "Kan inte skapa eller skriva till data-katalogen %s", "Invalid Federated Cloud ID" : "Ogiltigt Federerat Moln-ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Misslyckades dela ut %s då backend inte tillåter delningar från typ %i", "Sharing %s failed, because the file does not exist" : "Delning av %s misslyckades på grund av att filen inte existerar", "You are not allowed to share %s" : "Du har inte rätt att dela %s", "Sharing %s failed, because you can not share with yourself" : "Delning %s misslyckades därför att du inte kan dela med dig själv.", "Sharing %s failed, because the user %s does not exist" : "Delning %s misslyckades därför att användaren %s inte existerar", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Delning %s misslyckades därför att användaren %s inte är medlem i någon utav de grupper som %s är medlem i", "Sharing %s failed, because this item is already shared with %s" : "Delning %s misslyckades därför att objektet redan är delat med %s", "Sharing %s failed, because this item is already shared with user %s" : "Delning %s misslyckades därför att detta redan är delat med användaren %s", "Sharing %s failed, because the group %s does not exist" : "Delning %s misslyckades därför att gruppen %s inte existerar", "Sharing %s failed, because %s is not a member of the group %s" : "Delning %s misslyckades därför att %s inte ingår i gruppen %s", "You need to provide a password to create a public link, only protected links are allowed" : "Du måste ange ett lösenord för att skapa en offentlig länk, endast skyddade länkar är tillåtna", "Sharing %s failed, because sharing with links is not allowed" : "Delning %s misslyckades därför att delning utav länkar inte är tillåtet", "Not allowed to create a federated share with the same user" : "Ej tillåtet att skapa en federerad delning med samma användare", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Misslyckades dela ut %s, kan inte hitta %s, kanske är servern inte åtkomlig för närvarande.", "Share type %s is not valid for %s" : "Delningstyp %s är inte giltig för %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Kan inte sätta utgångsdatum. Utdelningar kan inte utgå senare än %s efter de har delats ut", "Cannot set expiration date. Expiration date is in the past" : "Kan inte sätta utgångsdatum. Utgångsdatumet är i det förflutna.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Delningsgränssnittet %s måste implementera gränssnittet OCP\\Share_Backend", "Sharing backend %s not found" : "Delningsgränssnittet %s hittades inte", "Sharing backend for %s not found" : "Delningsgränssnittet för %s hittades inte", "Sharing failed, because the user %s is the original sharer" : "Delning misslyckades eftersom användaren %s redan är den som har delat detta.", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Delning %s misslyckades därför att rättigheterna överskrider de rättigheter som är tillåtna för %s", "Sharing %s failed, because resharing is not allowed" : "Delning %s misslyckades därför att vidaredelning inte är tillåten", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Delning %s misslyckades därför att delningsgränsnittet för %s inte kunde hitta sin källa", "Sharing %s failed, because the file could not be found in the file cache" : "Delning %s misslyckades därför att filen inte kunde hittas i filcachen", "Can’t increase permissions of %s" : "Kan inte öka rättigheterna för %s", "Files can’t be shared with delete permissions" : "Filer kan inte delas med borttagningsrättigheter", "Files can’t be shared with create permissions" : "Filer kan inte delas med rättigheter att skapa", "Expiration date is in the past" : "Utgångsdatum är i det förflutna", "Can’t set expiration date more than %s days in the future" : "Kan inte sätta utgångsdatum mer än %s dagar framåt", "%s shared »%s« with you" : "%s delade »%s« med dig", "%s shared »%s« with you." : "%s delade »%s« med dig.", "Click the button below to open it." : "Klicka knappen nedan för att öppna det.", "Open »%s«" : "Öppna »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Den begärda delningen finns inte mer", "Could not find category \"%s\"" : "Kunde inte hitta kategorin \"%s\"", "Sunday" : "Söndag", "Monday" : "Måndag", "Tuesday" : "Tisdag", "Wednesday" : "Onsdag", "Thursday" : "Torsdag", "Friday" : "Fredag", "Saturday" : "Lördag", "Sun." : "Sön.", "Mon." : "Mån.", "Tue." : "Tis.", "Wed." : "Ons.", "Thu." : "Tors.", "Fri." : "Fre.", "Sat." : "Lör.", "Su" : "Sö", "Mo" : "Må", "Tu" : "Ti", "We" : "On", "Th" : "To", "Fr" : "Fr", "Sa" : "Lö", "January" : "Januari", "February" : "Februari", "March" : "Mars", "April" : "April", "May" : "Maj", "June" : "Juni", "July" : "Juli", "August" : "Augusti", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Maj.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Endast följande tecken är tillåtna i användarnamnet: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Ett giltigt användarnamn måste anges", "Username contains whitespace at the beginning or at the end" : "Användarnamnet består av ett mellanslag i början eller i slutet", "Username must not consist of dots only" : "Användarnamnet får inte innehålla enbart punkter", "A valid password must be provided" : "Ett giltigt lösenord måste anges", "The username is already being used" : "Användarnamnet används redan", "Could not create user" : "Kunde inte skapa användare", "User disabled" : "Användare inaktiverad", "Login canceled by app" : "Inloggningen avbruten av appen", "No app name specified" : "Inget appnamn angivet", "App '%s' could not be installed!" : "Applikationen \"%s\" gick inte att installera!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Applikationen \"%s\" kan ej installeras eftersom följande kriterier inte är uppfyllda: %s", "a safe home for all your data" : "En säker plats för dina filer och data", "File is currently busy, please try again later" : "Filen är för tillfället upptagen, vänligen försök igen senare", "Can't read file" : "Kan ej läsa filen", "Application is not enabled" : "Applikationen är inte aktiverad", "Authentication error" : "Fel vid autentisering", "Token expired. Please reload page." : "Ogiltig token. Ladda om sidan.", "Unknown user" : "Okänd användare", "No database drivers (sqlite, mysql, or postgresql) installed." : "Inga databasdrivrutiner (sqlite, mysql, eller postgresql) installerade.", "Cannot write into \"config\" directory" : "Kan inte skriva till \"config\" katalogen", "Cannot write into \"apps\" directory" : "Kan inte skriva till \"apps\" katalogen!", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Detta kan vanligtvis fixas genom att ge webbservern skrivåtkomst till mappen för appar eller genom att avaktivera App store i konfigurationsfilen. Se %s", "Cannot create \"data\" directory" : "Kan inte skapa \"datamapp\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Detta kan vanligtvis fixas genom att ge webbservern skrivåtkomst till rotkatalogen. Se %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Rättigheter kan vanligtvis fixas genom att ge webbservern skrivåtkomst till rotkatalogen. Se %s.", "Setting locale to %s failed" : "Sätta locale till %s misslyckades", "Please install one of these locales on your system and restart your webserver." : "Vänligen installera en av dessa locale på din server och starta om din webbserver,", "Please ask your server administrator to install the module." : "Vänligen be din administratör att installera modulen.", "PHP module %s not installed." : "PHP-modulen %s är inte installerad.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-inställning \"%s\" är inte inställd på \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Att ändra denna inställning i php.ini kommer göra så att Nextcloud fungerar igen", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload är satt till \"%s\" istället för det förväntade värdet \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "För att åtgärda detta problem sätt värdet <code> mbstring.func_overload till </ code> <code> 0 </ code> i din php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 är det minsta som krävs. För närvarande är %s installerat.", "To fix this issue update your libxml2 version and restart your web server." : "För att åtgärda detta problem uppdatera libxml2 versionen och starta om din webbserver.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP är tydligen inställt för att tömma \"inline doc blocks\". Detta kommer att göra flera kärnprogram otillgängliga.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Detta orsakas troligtvis av en cache/accelerator som t ex Zend OPchache eller eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-moduler har installerats, men de listas fortfarande som saknade?", "Please ask your server administrator to restart the web server." : "Vänligen be din serveradministratör att starta om webbservern.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 krävs", "Please upgrade your database version" : "Vänligen uppgradera din databas-version", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Vänligen ändra rättigheterna till 0770 så att katalogen inte kan listas utav andra användare.", "Your data directory is readable by other users" : "Din datamapp är läsbar av andra användare", "Your data directory must be an absolute path" : "Du måste specificera en korrekt sökväg till datamappen", "Check the value of \"datadirectory\" in your configuration" : "Kontrollera värdet av \"datakatalog\" i din konfiguration", "Your data directory is invalid" : "Din datamapp är ogiltig", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Säkerställ att du har filen \".ocdata\" i huvudkatalogen för din data.", "Could not obtain lock type %d on \"%s\"." : "Kunde inte hämta låstyp %d på \"%s\".", "Storage unauthorized. %s" : "Lagringsutrymme ej tillåtet. %s", "Storage incomplete configuration. %s" : "Lagringsutrymme felaktigt inställt. %s", "Storage connection error. %s" : "Lagringsutrymme lyckas inte ansluta. %s", "Storage is temporarily not available" : "Lagringsutrymme är för tillfället inte tillgängligt", "Storage connection timeout. %s" : "Lagringsutrymme lyckas inte ansluta \"timeout\". %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Detta kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till konfigurations-katalogen %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulen med id: %s finns inte. Vänligen aktivera det i dina app-inställningar eller kontakta din administratör.", "Server settings" : "Serverinställningar", "DB Error: \"%s\"" : "DB-fel: \"%s\"", "Offending command was: \"%s\"" : "Det felaktiga kommandot var: \"%s\"", "You need to enter either an existing account or the administrator." : "Du måste antingen ange ett befintligt konto eller administratör.", "Offending command was: \"%s\", name: %s, password: %s" : "Det felande kommandot var: \"%s\", namn: %s, lösenord: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Misslyckades att sätta rättigheter för %s därför att rättigheterna överskrider de som är tillåtna för %s", "Setting permissions for %s failed, because the item was not found" : "Att sätta rättigheterna för %s misslyckades därför att objektet inte hittades", "Cannot clear expiration date. Shares are required to have an expiration date." : "Kan ej ta bort utgångsdatumet. Delningen kräver att det finns ett utgångsdatum.", "Cannot increase permissions of %s" : "Kan ej öka behörigheterna för %s", "Files can't be shared with delete permissions" : "Filerna kan ej delas med \"radera behörigheter\"", "Files can't be shared with create permissions" : "Filerna kan ej delas med \"skapa behörigheter\"", "Cannot set expiration date more than %s days in the future" : "Kan ej välja ett utgångsdatum längre fram än %s dagar", "Personal" : "Personliga Inställningar", "Admin" : "Administration", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Detta kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till applikationskatalogen %s eller stänga av app-butik i konfigurationsfilen.", "Cannot create \"data\" directory (%s)" : "Kan inte skapa \"data\" katalog (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Detta kan vanligtvis åtgärdas genom att <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> ge webbserver skrivåtkomst till rotkatalogen </a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Rättigheterna kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till rotkatalogen %s.", "Data directory (%s) is readable by other users" : "Datakatalogen (%s) kan läsas av andra användare", "Data directory (%s) must be an absolute path" : "Datakatalogen (%s) måste vara hela sökvägen", "Data directory (%s) is invalid" : "Datamappen (%s) är ogiltig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Vänligen kontrollera att datakatalogen innehåller filen \".ocdata\" i rooten." }, "nplurals=2; plural=(n != 1);"); l10n/sq.json 0000604 00000056200 15247130447 0006642 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Nuk shkruhet dot te drejtoria \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Zakonisht kjo mund të ndreqet duke i akorduar shërbyesit web të drejta shkrimi mbi drejtorinë e formësimeve", "See %s" : "Shihni %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Kjo zakonisht mund të rregullohet duke i dhënë serverit të web-it akses shkrimi tek direktoria config. Shih %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Skedarët e aplikacionit %$1s nuk u zëvëndësuan në mënyrë korrekte. Sigurohuni që është një version që përputhet me serverin.", "Sample configuration detected" : "U gjet formësim shembull", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "U pa se është kopjuar shembulli për formësime. Kjo mund të prishë instalimin tuaj dhe nuk mbulohet. Ju lutemi, lexoni dokumentimin, përpara se të kryeni ndryshime te config.php", "%1$s and %2$s" : "%1$s dhe %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s dhe %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s dhe %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s dhe %5$s", "Education Edition" : "Variant Edukativ", "Enterprise bundle" : "Pakoja e ndërmarrjeve", "Groupware bundle" : "Pako groupware", "Social sharing bundle" : "Pakoja e ndarjes sociale", "PHP %s or higher is required." : "Kërkohet PHP %s ose më sipër.", "PHP with a version lower than %s is required." : "Lypset PHP me një version më të ulët se sa %s.", "%sbit or higher PHP required." : "Lypset PHP %sbit ose më i ri.", "Following databases are supported: %s" : "Mbulohen bazat vijuese të të dhënave: %s", "The command line tool %s could not be found" : "Mjeti rresht urdhrash %s s’u gjet dot", "The library %s is not available." : "Libraria %s s’është e passhme.", "Library %s with a version higher than %s is required - available version %s." : "Kërkohet librari %s me një version më të madh se %s - version gati %s.", "Library %s with a version lower than %s is required - available version %s." : "Lypset librari %s me një version më të vogël se %s - version gati %s.", "Following platforms are supported: %s" : "Mbulohen platformat vijuese: %s", "Server version %s or higher is required." : "Versioni i serverit kërkohet %s ose më lartë", "Server version %s or lower is required." : "Versioni i serverit kërkohet %s ose më poshtë", "Unknown filetype" : "Lloj i panjohur skedari", "Invalid image" : "Figurë e pavlefshme", "Avatar image is not square" : "Imazhi avatar nuk është katror", "today" : "sot", "yesterday" : "dje", "_%n day ago_::_%n days ago_" : ["%n ditë më parë","%n ditë më parë"], "last month" : "muajin e shkuar", "_%n month ago_::_%n months ago_" : ["%n muaj më parë","%n muaj më parë"], "last year" : "vitin e shkuar", "_%n year ago_::_%n years ago_" : ["%n vit më parë","%n vjet më parë"], "_%n hour ago_::_%n hours ago_" : ["%n orë më parë","%n orë më parë"], "_%n minute ago_::_%n minutes ago_" : ["%n minutë më parë","%n minuta më parë"], "seconds ago" : "sekonda më parë", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduli me ID: %s nuk ekziston. Ju lutem aktivizojeni atë në konfigurimet e aplikacionit tuaj ose kontaktoni administratorin tuaj.", "File name is a reserved word" : "Emri i kartelës është një emër i rezervuar", "File name contains at least one invalid character" : "Emri i kartelës përmban të paktën një shenjë të pavlefshme", "File name is too long" : "Emri i kartelës është shumë i gjatë", "Dot files are not allowed" : "Nuk lejohen kartela të fshehura", "Empty filename is not allowed" : "Nuk lejohen emra të zbrazët kartelash", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Aplikacioni \"%s\" s’mund të instalohet, ngaqë s’lexohet dot kartela appinfo.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikacioni \"%s\" nuk mund të instalohet sepse nuk përputhet me këtë version të serverit.", "This is an automatically sent email, please do not reply." : "Ky është një email i dërguar automatikisht, ju lutem mos u përgjigjni.", "Help" : "Ndihmë", "Apps" : "Aplikacione", "Settings" : "Konfigurime", "Log out" : "Shkyçu", "Users" : "Përdorues", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Konfigurime bazike", "Sharing" : "Ndarja", "Security" : "Siguria", "Encryption" : "Enkriptimi", "Additional settings" : "Konfigurime shtesë", "Tips & tricks" : "Këshilla dhe rrengje", "Personal info" : "Informacion personal", "Sync clients" : "Klientë të sikronizuar", "Unlimited" : "E palimituar", "__language_name__" : "_emri_i_gjuhës__", "Verifying" : "Duke e verifikuar", "Verifying …" : "Duke e verifikuar ...", "Verify" : "Verifiko", "%s enter the database username and name." : "%s jepni emrin e bazës së të dhënave dhe emrin e përdoruesit për të.", "%s enter the database username." : "%s jepni emrin e përdoruesit të bazës së të dhënave.", "%s enter the database name." : "%s jepni emrin e bazës së të dhënave.", "%s you may not use dots in the database name" : "%s s’mund të përdorni pika te emri i bazës së të dhënave", "Oracle connection could not be established" : "S’u vendos dot lidhje me Oracle", "Oracle username and/or password not valid" : "Emër përdoruesi dhe/ose fjalëkalim Oracle-i i pavlefshëm", "PostgreSQL username and/or password not valid" : "Emër përdoruesi dhe/ose fjalëkalim PostgreSQL jo të vlefshëm", "You need to enter details of an existing account." : "Duhet të futni detajet e një llogarie ekzistuese.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nuk mbulohet dhe %s s’do të funksionojë si duhet në këtë platformë. Përdoreni nën përgjegjësinë tuaj! ", "For the best results, please consider using a GNU/Linux server instead." : "Për përfundimet më të mira, ju lutemi, më mirë konsideroni përdorimin e një shërbyesi GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Duket se kjo instancë %s xhiron një mjedis PHP 32-bitësh dhe open_basedir është e formësuar, te php.ini. Kjo do të shpjerë në probleme me kartela më të mëdha se 4 GB dhe këshillohet me forcë të mos ndodhë.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Ju lutemi, hiqeni rregullimin open_basedir nga php.ini juaj ose hidhuni te PHP për 64-bit.", "Set an admin username." : "Caktoni një emër përdoruesi për përgjegjësin.", "Set an admin password." : "Caktoni një fjalëkalim për përgjegjësin.", "Can't create or write into the data directory %s" : "S’e krijon ose s’shkruan dot te drejtoria e të dhënave %s", "Invalid Federated Cloud ID" : "ID Federated Cloud e pavlefshme", "Sharing %s failed, because the backend does not allow shares from type %i" : "Ndarja e %s dështoi, ngaqë pjesa përgjegjëse e shërbyesit nuk lejon ndarje prej llojit %i", "Sharing %s failed, because the file does not exist" : "Ndarja e %s me të tjerët dështoi, ngaqë kartela s’ekziston", "You are not allowed to share %s" : "Nuk ju lejohet ta ndani %s me të tjerët", "Sharing %s failed, because you can not share with yourself" : "Ndarja e %s dështoi, ngaqë s’mund të ndani gjëra me vetveten", "Sharing %s failed, because the user %s does not exist" : "Ndarja e %s me të tjerët dështoi, ngaqë përdoruesi %s nuk ekziston", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Ndarja për %s dështoi, ngaqë përdoruesi %s s’është anëtar i ndonjë grupi ku %s është anëtar", "Sharing %s failed, because this item is already shared with %s" : "Ndarja për %s dështoi, ngaqë ky objekt është ndarë një herë me %s", "Sharing %s failed, because this item is already shared with user %s" : "Ndarja e %s me të tjerët dështoi, ngaqë ky objekt është ndarë tashmë me përdoruesin %s", "Sharing %s failed, because the group %s does not exist" : "Ndarja e %s me të tjerët dështoi, ngaqë grupi %s nuk ekziston", "Sharing %s failed, because %s is not a member of the group %s" : "Ndarja e %s me të tjerët dështoi, ngaqë %s s’është anëtar i grupit %s", "You need to provide a password to create a public link, only protected links are allowed" : "Lypset të jepni një fjalëkalim që të krijoni një lidhje publike, lejohen vetëm lidhje të mbrojtura", "Sharing %s failed, because sharing with links is not allowed" : "Ndarja e %s me të tjerët dështoi, ngaqë nuk lejohet ndarja me lidhje", "Not allowed to create a federated share with the same user" : "S’i lejohet të krijojë një ndarje të federuar me të njëjtin përdorues", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Ndarja për %s dështoi, s’u gjet dot %s, ndoshta shërbyesi është hëpërhë jashtë pune.", "Share type %s is not valid for %s" : "Lloji i ndarjes %s s’është i vlefshëm për %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "S’caktohet dot data e skadimit. Ndarjet s’mund të skadojnë më vonë se %s pasi të jenë ofruar", "Cannot set expiration date. Expiration date is in the past" : "S’caktohet dot data e skadimit. Data e skadimit bie në të kaluarën", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Mekanizmi i shërbimit për ndarje %s duhet të sendërtojë ndërfaqen OCP\\Share_Backend", "Sharing backend %s not found" : "S’u gjet mekanizmi i shërbimit për ndarje %s", "Sharing backend for %s not found" : "S’u gjet mekanizmi i shërbimit për ndarje për %s", "Sharing failed, because the user %s is the original sharer" : "Ndarja dështoi, ngaqë përdoruesi %s është ai që e ndau fillimisht", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Ndarja e %s me të tjerët dështoi, ngaqë lejet tejkalojnë lejet e akorduara për %s", "Sharing %s failed, because resharing is not allowed" : "Ndarja e %s me të tjerët dështoi, ngaqë nuk lejohen rindarje", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Ndarja e %s dështoi, ngaqë mekanizmi i shërbimit për ndarje për %s s’gjeti dot burimin për të", "Sharing %s failed, because the file could not be found in the file cache" : "Ndarja e %s me të tjerët dështoi, ngaqë kartela s’u gjet dot te fshehtina e kartelave", "Can’t increase permissions of %s" : "Nuk mund të shtohen lejet e %s", "Files can’t be shared with delete permissions" : "Skedarët nuk mund të ndahen me leje të fshira", "Files can’t be shared with create permissions" : "matchSkedarët nuk mund të ndahen me leje të krijuara", "Expiration date is in the past" : "Data e skadimit bie në të kaluarën", "Can’t set expiration date more than %s days in the future" : "Nuk mund të caktohet data e skadimit më shumë se %s ditë në të ardhmen", "%s shared »%s« with you" : "%s ndau me ju »%s«", "%s shared »%s« with you." : "1 %s ndarë »1 %s« me ju.", "Click the button below to open it." : "Kliko butonin më poshtë për të hapur atë.", "Open »%s«" : "Hap»1 %s«", "%s via %s" : "%s përmes %s", "The requested share does not exist anymore" : "Ndarja e kërkuar nuk ekziston më", "Could not find category \"%s\"" : "S’u gjet kategori \"%s\"", "Sunday" : "E Dielë", "Monday" : "E Hënë", "Tuesday" : "E Martë", "Wednesday" : "E Mërkurë", "Thursday" : "E Enjte", "Friday" : "E Premte", "Saturday" : "E Shtunë", "Sun." : "Die.", "Mon." : "Hën.", "Tue." : "Mar.", "Wed." : "Mër.", "Thu." : "Enj.", "Fri." : "Pre.", "Sat." : "Sht.", "Su" : "Di", "Mo" : "Hë", "Tu" : "Ma", "We" : "Ne", "Th" : "En", "Fr" : "Pr", "Sa" : "Sh", "January" : "Janar", "February" : "Shkurt", "March" : "Mars", "April" : "Prill", "May" : "Maj", "June" : "Qershor", "July" : "Korrik", "August" : "Gusht", "September" : "Shtator", "October" : "Tetor", "November" : "Nëntor", "December" : "Dhjetor", "Jan." : "Jan.", "Feb." : "Shk.", "Mar." : "Mar.", "Apr." : "Pri.", "May." : "Maj.", "Jun." : "Qer.", "Jul." : "Kor.", "Aug." : "Gus.", "Sep." : "Sht.", "Oct." : "Tet.", "Nov." : "Nën.", "Dec." : "Dhj.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Në një emër përdoruesi lejohen vetëm shenjat vijuese: \"a-z\", \"A-Z\", \"0-9\", dhe \"_.@-\"", "A valid username must be provided" : "Duhet dhënë një emër i vlefshëm përdoruesi", "Username contains whitespace at the beginning or at the end" : "Emri i përdoruesit përmban hapësirë në fillim ose në fund", "Username must not consist of dots only" : "Emri i përdoruesit nuk duhet të përbëhet vetëm nga pika", "A valid password must be provided" : "Duhet dhënë një fjalëkalim i vlefshëm", "The username is already being used" : "Emri i përdoruesit është tashmë i përdorur", "User disabled" : "Përdorues i çaktivizuar", "Login canceled by app" : "Hyrja u anulua nga aplikacioni", "No app name specified" : "S’u dha emër aplikacioni", "App '%s' could not be installed!" : "Aplikacioni \"%s\" nuk mund të instalohet!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Përditësimi \"%s\" s’instalohet dot, ngaqë s’plotësohen varësitë vijuese: %s.", "a safe home for all your data" : "Një shtëpi e sigurt për të dhënat e tua", "File is currently busy, please try again later" : "Kartela tani është e zënë, ju lutemi, riprovoni më vonë.", "Can't read file" : "S'lexohet dot kartela", "Application is not enabled" : "Aplikacioni s’është aktivizuar", "Authentication error" : "Gabim mirëfilltësimi", "Token expired. Please reload page." : "Token-i ka skaduar. Ju lutem ringarkoni faqen.", "Unknown user" : "Përdorues i panjohur", "No database drivers (sqlite, mysql, or postgresql) installed." : "S’ka baza të dhënash (sqlite, mysql, ose postgresql) të instaluara.", "Cannot write into \"config\" directory" : "S’shkruhet dot te drejtoria \"config\"", "Cannot write into \"apps\" directory" : "S’shkruhet dot te drejtoria \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Zakonisht kjo mund të rregullohet duke i dhënë serverit të web-it akses shkrimi tek direktoria e aplikacioneve ose duke çaktivizuar appstore në skedarin config. Shih %s", "Cannot create \"data\" directory" : "Nuk mund të krijohet direktoria \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Kjo zakonisht mund të rregullohet duke i dhënë serverit të web-it akses shkrimi tek direktoria rrënjë. Shih %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Zakonisht lejet mund të rregullohen duke i dhënë serverit të web-it akses shkrimi tek direktoria rrënjë. Shih %s.", "Setting locale to %s failed" : "Caktimi i gjuhës si %s dështoi", "Please install one of these locales on your system and restart your webserver." : "Ju lutemi, instaloni te sistemi juaj një prej këtyre vendoreve dhe rinisni shërbyesin tuaj web.", "Please ask your server administrator to install the module." : "Ju lutemi, kërkojini përgjegjësit të shërbyesit ta instalojë modulin.", "PHP module %s not installed." : "Moduli PHP %s s’është i instaluar.", "PHP setting \"%s\" is not set to \"%s\"." : "Rregullimi PHP \"%s\" s’është vënë si \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Përshtatja e këtij konfigurimi në php.ini do e bëjë Nextcloud të punoj përsëri", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload është caktuar si \"%s\", në vend të vlerës së pritshme \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Për ta ndrequr këtë problem, caktoni për <code>mbstring.func_overload</code> vlerën <code>0</code> te php.ini juaj", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Lypset të paktën libxml2 2.7.0. Hëpërhë e instaluar është %s.", "To fix this issue update your libxml2 version and restart your web server." : "Për të ndrequr këtë problem, përditësoni libxml2 dhe rinisni shërbyesin tuaj web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Me sa duket, PHP-ja është rregulluar që të heqë blloqe të brendshëm dokumentimi. Kjo do t’i nxjerrë nga funksionimi disa aplikacione bazë.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Kjo ka gjasa të jetë shkaktuar nga një fshehtinë/përshpejtues i tillë si Zend OPcache ose eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Modulet PHP janë instaluar, por tregohen ende sikur mungojnë?", "Please ask your server administrator to restart the web server." : "Ju lutemi, kërkojini përgjegjësit të shërbyesit tuaj të rinisë shërbyesin web.", "PostgreSQL >= 9 required" : "Lypset PostgreSQL >= 9", "Please upgrade your database version" : "Ju lutemi, përmirësoni bazën tuaj të të dhënave me një version më të ri.", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Ju lutemi, kalojani lejet në 0770, që kështu atë drejtori të mos mund ta shfaqin përdorues të tjerë.", "Your data directory is readable by other users" : "Direktoria juaj e të dhënave është e lexueshme nga përdorues të tjerë", "Your data directory must be an absolute path" : "Direktoria juaj e të dhënave duhet të jetë një path absolut", "Check the value of \"datadirectory\" in your configuration" : "Kontrolloni vlerën e \"datadirectory\" te formësimi juaj", "Your data directory is invalid" : "Direktoria juaj e të dhënave është i pavlefshëm", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Sigurohu që ekziston një skedar i quajtur \".ocdata\" në rrënjën e direktorisë së të dhënave.", "Could not obtain lock type %d on \"%s\"." : "S’u mor dot lloj kyçjeje %d në \"%s\".", "Storage unauthorized. %s" : "Depozitë e paautorizuar. %s", "Storage incomplete configuration. %s" : "Formësim jo i plotë i depozitës. %s", "Storage connection error. %s" : "Gabim lidhje te depozita. %s", "Storage is temporarily not available" : "Hapsira ruajtëse nuk është në dispozicion përkohësisht", "Storage connection timeout. %s" : "Mbarim kohe lidhjeje për depozitën. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Zakonisht kjo mund të ndreqet duke %si akorduar shërbyesit web të drejta shkrimi mbi drejtorinë e formësimeve%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "S’ka modul me id: %s. Ju lutemi, aktivizojeni te rregullimet tuaja për aplikacionin ose lidhuni me përgjegjësin tuaj.", "Server settings" : "Konfigurimi i serverit", "DB Error: \"%s\"" : "Gabim DB-je: \"%s\"", "Offending command was: \"%s\"" : "Urdhëri shkaktar ishte: \"%s\"", "You need to enter either an existing account or the administrator." : "Lypset të jepni ose një llogari ekzistuese, ose llogarinë e përgjegjësit.", "Offending command was: \"%s\", name: %s, password: %s" : "Urdhri shkaktar qe: \"%s\", emër: %s, fjalëkalim: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Caktimi i lejeve për %s dështoi, ngaqë lejet tejkalojnë lejet e akorduara për %s", "Setting permissions for %s failed, because the item was not found" : "Caktimi i lejeve për %s dështoi, ngaqë s’u gjet objekti", "Cannot clear expiration date. Shares are required to have an expiration date." : "S’hiqet dot data e skadimit. Ndarjet lypse të kenë një datë skadimi.", "Cannot increase permissions of %s" : "S’mund të shtohen lejet për %s", "Files can't be shared with delete permissions" : "Kartelat s’mund të ndahen me leje fshirjeje", "Files can't be shared with create permissions" : "Kartelat s’mund të ndahen me leje krijimi", "Cannot set expiration date more than %s days in the future" : "S’mund të caktohet data e skadimit më shumë se %s ditë në të ardhmen", "Personal" : "Personale", "Admin" : "Admin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Zakonisht kjo mund të ndreqet duke %si akorduar shërbyesit web të drejta shkrimi mbi drejtorinë e aplikacionit%s ose duke e çaktivizuar appstore-in te kartela e formësimit.", "Cannot create \"data\" directory (%s)" : "S’krijohet dot drejtoria \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Zakonisht kjo mund të ndreqet duke <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">i akorduar shërbyesit web të drejta shkrimi mbi drejtorinë rrënjë</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Zakonisht lejet mund të ndreqen duke %si akorduar shërbyesit web të drejta shkrimi mbi drejtorinë rrënjë%s.", "Data directory (%s) is readable by other users" : "Drejtoria e të dhënave (%s) është e lexueshme nga përdorues të tjerë", "Data directory (%s) must be an absolute path" : "Drejtoria e të dhënave (%s) duhet të jepë një shteg absolut", "Data directory (%s) is invalid" : "Drejtoria e të dhënave (%s) është e pavlefshme", "Please check that the data directory contains a file \".ocdata\" in its root." : "Ju lutemi, kontrolloni që drejtoria e të dhënave përmban në rrënjën e saj një kartelë \".ocdata\"." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/fr.js 0000604 00000057431 15247130447 0006300 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Impossible d’écrire dans le répertoire « config » !", "This can usually be fixed by giving the webserver write access to the config directory" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire \"config\"", "See %s" : "Voir %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire \"config\". Voir %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Les fichiers de l'application %$1s n'ont pas été remplacés correctement. Veuillez vérifier que c'est une version compatible avec le serveur.", "Sample configuration detected" : "Configuration d'exemple détectée", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Il a été détecté que la configuration donnée à titre d'exemple a été copiée. Cela peut rendre votre installation inopérante et n'est pas pris en charge. Veuillez lire la documentation avant d'effectuer des modifications dans config.php", "%1$s and %2$s" : "%1$s et %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s et %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s et %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s et %5$s", "Education Edition" : "Édition pour l'éducation ", "Enterprise bundle" : "Pack pour entreprise", "Groupware bundle" : "Pack pour travail collaboratif", "Social sharing bundle" : "Pack pour partage social", "PHP %s or higher is required." : "PHP %s ou supérieur est requis.", "PHP with a version lower than %s is required." : "PHP avec une version antérieure à %s est requis.", "%sbit or higher PHP required." : "PHP %sbits ou supérieur est requis.", "Following databases are supported: %s" : "Les bases de données suivantes sont supportées : %s", "The command line tool %s could not be found" : "La commande %s est introuvable", "The library %s is not available." : "La librairie %s n'est pas disponible.", "Library %s with a version higher than %s is required - available version %s." : "La librairie %s doit être au moins à la version %s. Version disponible : %s.", "Library %s with a version lower than %s is required - available version %s." : "La librairie %s doit avoir une version antérieure à %s. Version disponible : %s.", "Following platforms are supported: %s" : "Les plateformes suivantes sont prises en charge : %s", "Server version %s or higher is required." : "Un serveur de version %s ou supérieure est requis.", "Server version %s or lower is required." : "Un serveur de version %s ou inférieure est requis.", "Unknown filetype" : "Type de fichier inconnu", "Invalid image" : "Image non valable", "Avatar image is not square" : "L'image d'avatar n'est pas carré", "today" : "aujourd'hui", "yesterday" : "hier", "_%n day ago_::_%n days ago_" : ["il y a %n jour","il y a %n jours"], "last month" : "le mois dernier", "_%n month ago_::_%n months ago_" : ["Il y a %n mois","Il y a %n mois"], "last year" : "l'année dernière", "_%n year ago_::_%n years ago_" : ["il y a %n an","il y a %n ans"], "_%n hour ago_::_%n hours ago_" : ["Il y a %n heure","Il y a %n heures"], "_%n minute ago_::_%n minutes ago_" : ["il y a %n minute","il y a %n minutes"], "seconds ago" : "il y a quelques secondes", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Le module avec l'ID: %s n'existe pas. Merci de l'activer dans les paramètres d'applications ou de contacter votre administrateur.", "File name is a reserved word" : "Ce nom de fichier est un mot réservé", "File name contains at least one invalid character" : "Le nom de fichier contient un (des) caractère(s) non valide(s)", "File name is too long" : "Nom de fichier trop long", "Dot files are not allowed" : "Le nom de fichier ne peut pas commencer par un point", "Empty filename is not allowed" : "Le nom de fichier ne peut pas être vide", "App \"%s\" cannot be installed because appinfo file cannot be read." : "L'application \"%s\" ne peut pas être installée car le fichier appinfo ne peut pas être lu.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "L'application \"%s\" ne peut être installée car elle n'est pas compatible avec cette version du serveur", "This is an automatically sent email, please do not reply." : "Ceci est un e-mail envoyé automatiquement, veuillez ne pas y répondre.", "Help" : "Aide", "Apps" : "Applications", "Settings" : "Paramètres", "Log out" : "Se déconnecter", "Users" : "Utilisateurs", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Paramètres de base", "Sharing" : "Partage", "Security" : "Sécurité", "Encryption" : "Chiffrement", "Additional settings" : "Paramètres supplémentaires", "Tips & tricks" : "Trucs et astuces", "Personal info" : "Informations personnelles", "Sync clients" : "Clients de synchronisation", "Unlimited" : "Illimité", "__language_name__" : "Français", "Verifying" : "Vérification en cours", "Verifying …" : "Vérification en cours...", "Verify" : "Vérifié", "%s enter the database username and name." : "%s entrez le nom d'utilisateur et le nom de la base de données.", "%s enter the database username." : "%s entrez le nom d'utilisateur de la base de données.", "%s enter the database name." : "%s entrez le nom de la base de données.", "%s you may not use dots in the database name" : "%s vous ne pouvez pas utiliser de points dans le nom de la base de données", "Oracle connection could not be established" : "La connexion Oracle ne peut être établie", "Oracle username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base Oracle non valide(s)", "PostgreSQL username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base PostgreSQL non valide(s)", "You need to enter details of an existing account." : "Vous devez indiquer les détails d'un compte existant.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X n'est pas pris en charge et %s ne fonctionnera pas correctement sur cette plate-forme. Son utilisation est à vos risques et périls !", "For the best results, please consider using a GNU/Linux server instead." : "Pour obtenir les meilleurs résultats, vous devriez utiliser un serveur GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Il semble que cette instance %s fonctionne sur un environnement PHP 32-bit et open_basedir a été configuré dans php.ini. Cela engendre des problèmes avec les fichiers de taille supérieure à 4 Go et est donc fortement déconseillé.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Veuillez supprimer la configuration open_basedir de votre php.ini ou utiliser une version PHP 64-bit.", "Set an admin username." : "Spécifiez un nom d'utilisateur pour l'administrateur.", "Set an admin password." : "Spécifiez un mot de passe pour l'administrateur.", "Can't create or write into the data directory %s" : "Impossible de créer, ou d'écrire dans, le répertoire des données %s", "Invalid Federated Cloud ID" : "ID Federated Cloud incorrect", "Sharing %s failed, because the backend does not allow shares from type %i" : "Le partage de %s a échoué car l’infrastructure n'autorise pas les partages de type %i", "Sharing %s failed, because the file does not exist" : "Le partage de %s a échoué car le fichier n'existe pas", "You are not allowed to share %s" : "Vous n'êtes pas autorisé à partager %s", "Sharing %s failed, because you can not share with yourself" : "Le partage de %s a échoué car vous ne pouvez pas partager avec vous-même", "Sharing %s failed, because the user %s does not exist" : "Le partage de %s a échoué car l'utilisateur %s n'existe pas", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Le partage de %s a échoué car l'utilisateur %s n'est membre d'aucun groupe auquel %s appartient", "Sharing %s failed, because this item is already shared with %s" : "Le partage de %s a échoué car cet objet est déjà partagé avec %s", "Sharing %s failed, because this item is already shared with user %s" : "Le partage de %s a échoué car cet élément est déjà partagé avec l'utilisateur %s", "Sharing %s failed, because the group %s does not exist" : "Le partage de %s a échoué car le groupe %s n'existe pas", "Sharing %s failed, because %s is not a member of the group %s" : "Le partage de %s a échoué car %s n'est pas membre du groupe %s", "You need to provide a password to create a public link, only protected links are allowed" : "Vous devez fournir un mot de passe pour créer un lien public, seuls les liens protégés sont autorisées.", "Sharing %s failed, because sharing with links is not allowed" : "Le partage de %s a échoué car le partage par lien n'est pas permis", "Not allowed to create a federated share with the same user" : "Non autorisé à créer un partage fédéré avec le même utilisateur", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Le partage de %s a échoué : impossible de trouver %s. Peut-être le serveur est-il momentanément injoignable.", "Share type %s is not valid for %s" : "Le type de partage %s n'est pas valide pour %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Impossible de configurer la date d'expiration. Un partage ne peut expirer plus de %s après sa création", "Cannot set expiration date. Expiration date is in the past" : "Impossible de configurer la date d'expiration : elle est dans le passé.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Le service de partage %s doit implémenter l'interface OCP\\Share_Backend", "Sharing backend %s not found" : "Service de partage %s non trouvé", "Sharing backend for %s not found" : "Le service de partage pour %s est introuvable", "Sharing failed, because the user %s is the original sharer" : "Le partage a échoué car l'utilisateur %s est le propriétaire original", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Le partage de %s a échoué car les permissions dépassent celles accordées à %s", "Sharing %s failed, because resharing is not allowed" : "Le partage de %s a échoué car le repartage n'est pas autorisé", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Le partage de %s a échoué car le service %s n'a pas trouvé sa source..", "Sharing %s failed, because the file could not be found in the file cache" : "Le partage de %s a échoué car le fichier n'a pas été trouvé dans les fichiers mis en cache.", "Can’t increase permissions of %s" : "Impossible d'augmenter les permissions de %s", "Files can’t be shared with delete permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de suppression", "Files can’t be shared with create permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de création", "Expiration date is in the past" : "La date d'expiration est dans le passé", "Can’t set expiration date more than %s days in the future" : "Impossible de définir la date d'expiration à plus de %s jours dans le futur", "%s shared »%s« with you" : "%s a partagé «%s» avec vous", "%s shared »%s« with you." : "%s a partagé «%s» avec vous.", "Click the button below to open it." : "Cliquez sur le bouton ci-dessous pour l'ouvrir", "Open »%s«" : "Ouvrir «%s»", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Le partage demandé n'existe plus", "Could not find category \"%s\"" : "Impossible de trouver la catégorie \"%s\"", "Sunday" : "Dimanche", "Monday" : "Lundi", "Tuesday" : "Mardi", "Wednesday" : "Mercredi", "Thursday" : "Jeudi", "Friday" : "Vendredi", "Saturday" : "Samedi", "Sun." : "Dim.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mer.", "Thu." : "Jeu.", "Fri." : "Ven.", "Sat." : "Sam.", "Su" : "Di", "Mo" : "Lu", "Tu" : "Ma", "We" : "Me", "Th" : "Je", "Fr" : "Ve", "Sa" : "Sa", "January" : "Janvier", "February" : "Février", "March" : "Mars", "April" : "Avril", "May" : "Mai", "June" : "Juin", "July" : "Juillet", "August" : "Août", "September" : "Septembre", "October" : "Octobre", "November" : "Novembre", "December" : "Décembre", "Jan." : "Jan.", "Feb." : "Fév.", "Mar." : "Mars", "Apr." : "Avr.", "May." : "Mai", "Jun." : "Juin", "Jul." : "Juil.", "Aug." : "Août", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Déc.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Seuls les caractères suivants sont autorisés dans un nom d'utilisateur : \"a-z\", \"A-Z\", \"0-9\", \"_@-\" et \".\" (le point)", "A valid username must be provided" : "Un nom d'utilisateur valide doit être saisi", "Username contains whitespace at the beginning or at the end" : "Le nom d'utilisateur contient des espaces au début ou à la fin", "Username must not consist of dots only" : "Le nom d'utilisateur ne doit pas être composé uniquement de points", "A valid password must be provided" : "Un mot de passe valide doit être saisi", "The username is already being used" : "Ce nom d'utilisateur est déjà utilisé", "Could not create user" : "Impossible de créer l'utilisateur", "User disabled" : "Utilisateur désactivé", "Login canceled by app" : "L'authentification a été annulé par l'application", "No app name specified" : "Aucun nom d'application spécifié", "App '%s' could not be installed!" : "L'application \"%s\" ne peut pas être installée !", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "L'application \"%s\" ne peut pas être installée à cause des dépendances suivantes non satisfaites : %s", "a safe home for all your data" : "un endroit sûr pour toutes vos données", "File is currently busy, please try again later" : "Le fichier est actuellement utilisé, veuillez réessayer plus tard", "Can't read file" : "Impossible de lire le fichier", "Application is not enabled" : "L'application n'est pas activée", "Authentication error" : "Erreur d'authentification", "Token expired. Please reload page." : "La session a expiré. Veuillez recharger la page.", "Unknown user" : "Utilisateur inconnu", "No database drivers (sqlite, mysql, or postgresql) installed." : "Aucun pilote de base de données n’est installé (sqlite, mysql ou postgresql).", "Cannot write into \"config\" directory" : "Impossible d’écrire dans le répertoire \"config\"", "Cannot write into \"apps\" directory" : "Impossible d’écrire dans le répertoire \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire \"apps\" ou en désactivant l'appstore dans le fichier de configuration. Voir %s", "Cannot create \"data\" directory" : "Impossible de créer le dossier \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire racine. Voir %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Le problème de permissions peut généralement être résolu en donnant au serveur web un accès en écriture au répertoire racine. Voir %s.", "Setting locale to %s failed" : "Echec de la spécification des paramètres régionaux à %s", "Please install one of these locales on your system and restart your webserver." : "Veuillez installer l'un de ces paramètres régionaux sur votre système et redémarrer votre serveur web.", "Please ask your server administrator to install the module." : "Veuillez demander à votre administrateur d’installer le module.", "PHP module %s not installed." : "Le module PHP %s n’est pas installé.", "PHP setting \"%s\" is not set to \"%s\"." : "Le paramètre PHP \"%s\" n'est pas \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ajuster ce paramètre dans php.ini fera fonctionner Nextcould à nouveau", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload est à \"%s\" alors que la valeur \"0\" est attendue", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Pour corriger ce problème mettez <code>mbstring.func_overload</code> à <code>0</code> dans votre php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 au moins est requis. Actuellement %s est installé.", "To fix this issue update your libxml2 version and restart your web server." : "Pour régler ce problème, mettez à jour votre version de libxml2 et redémarrez votre serveur web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP semble configuré de manière à supprimer les blocs PHPdoc du code. Cela rendra plusieurs applications de base inaccessibles.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "La raison est probablement l'utilisation d'un cache / accélérateur tel que Zend OPcache ou eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Les modules PHP ont été installés mais sont toujours indiqués comme manquants ?", "Please ask your server administrator to restart the web server." : "Veuillez demander à votre administrateur serveur de redémarrer le serveur web.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 requis", "Please upgrade your database version" : "Veuillez mettre à jour votre gestionnaire de base de données", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Veuillez changer les permissions du répertoire en mode 0770 afin que son contenu ne puisse pas être listé par les autres utilisateurs.", "Your data directory is readable by other users" : "Votre répertoire est lisible par les autres utilisateurs", "Your data directory must be an absolute path" : "Le chemin de votre répertoire doit être un lien absolu", "Check the value of \"datadirectory\" in your configuration" : "Verifiez la valeur de \"datadirectory\" dans votre configuration", "Your data directory is invalid" : "Votre répertoire n'est pas valide", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Assurez-vous que le répertoire de données contient un fichier \".ocdata\" à sa racine.", "Could not obtain lock type %d on \"%s\"." : "Impossible d'obtenir le verrouillage de type %d sur \"%s\".", "Storage unauthorized. %s" : "Espace de stockage non autorisé. %s", "Storage incomplete configuration. %s" : "Configuration de l'espace de stockage incomplète. %s", "Storage connection error. %s" : "Erreur de connexion à l'espace stockage. %s", "Storage is temporarily not available" : "Le support de stockage est temporairement indisponible", "Storage connection timeout. %s" : "Le délai d'attente pour la connexion à l'espace de stockage a été dépassé. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Ce problème est généralement résolu %sen donnant au serveur web un accès en écriture au répertoire de configuration%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Le module avec l'id: %s n'existe pas. Merci de l'activer dans les paramètres d'applications ou de contacter votre administrateur.", "Server settings" : "Paramètres serveur", "DB Error: \"%s\"" : "Erreur de la base de données : \"%s\"", "Offending command was: \"%s\"" : "La requête en cause est : \"%s\"", "You need to enter either an existing account or the administrator." : "Vous devez indiquer un compte existant ou celui de l'administrateur.", "Offending command was: \"%s\", name: %s, password: %s" : "La requête en cause est : \"%s\", nom : %s, mot de passe : %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Le réglage des permissions pour %s a échoué car les permissions dépassent celles accordées à %s", "Setting permissions for %s failed, because the item was not found" : "Le réglage des permissions pour %s a échoué car l'objet n'a pas été trouvé", "Cannot clear expiration date. Shares are required to have an expiration date." : "Impossible de supprimer la date d'expiration. Les partages doivent avoir une date d'expiration.", "Cannot increase permissions of %s" : "Impossible d'augmenter les permissions de %s", "Files can't be shared with delete permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de suppression", "Files can't be shared with create permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de création", "Cannot set expiration date more than %s days in the future" : "Impossible de définir la date d'expiration à plus de %s jours dans le futur", "Personal" : "Personnel", "Admin" : "Administration", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Ce problème est généralement résolu %sen donnant au serveur web un accès en écriture au répertoire apps%s ou en désactivant l'appstore dans le fichier de configuration.", "Cannot create \"data\" directory (%s)" : "Impossible de créer le répertoire \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Ce problème est généralement résolu <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">en donnant au serveur web un accès en écriture au répertoire racine</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Le problème de permissions peut généralement être résolu %sen donnant au serveur web un accès en écriture au répertoire racine%s", "Data directory (%s) is readable by other users" : "Le répertoire de données (%s) est lisible par les autres utilisateurs", "Data directory (%s) must be an absolute path" : "Le chemin du dossier de données (%s) doit être absolu", "Data directory (%s) is invalid" : "Le répertoire (%s) n'est pas valide", "Please check that the data directory contains a file \".ocdata\" in its root." : "Veuillez vérifier que le répertoire de données contient un fichier \".ocdata\" à sa racine." }, "nplurals=2; plural=(n > 1);"); l10n/pt_BR.js 0000604 00000056366 15247130447 0006705 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Não é possível gravar no diretório \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Isso geralmente pode ser corrigido dando o acesso de escritura ao webserver para o diretório de configuração", "See %s" : "Ver %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Normalmente isso pode ser resolvido dando ao webserver permissão de escritura no diretório config. Veja %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Os arquivos do aplicativo %$1s não foram substituídos corretamente. Certifique-se de que é uma versão compatível com o servidor.", "Sample configuration detected" : "Configuração de exemplo detectada", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Foi detectado que a configuração de exemplo foi copiada. Isso pode terminar sua instalação e não é suportado. Por favor leia a documentação antes de realizar mudanças no config.php", "%1$s and %2$s" : "%1$s e %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s e %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s e %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s e %5$s", "Education Edition" : "Edição Educativa", "Enterprise bundle" : "Pacote Enterprise", "Groupware bundle" : "Pacote Groupware", "Social sharing bundle" : "Pacote de compartilhamento social", "PHP %s or higher is required." : "PHP %s ou superior é requerido.", "PHP with a version lower than %s is required." : "É requerida uma versão PHP mais antiga que a %s .", "%sbit or higher PHP required." : "%sbit ou PHP maior é requerido.", "Following databases are supported: %s" : "Os seguintes bancos de dados são suportados: %s", "The command line tool %s could not be found" : "A ferramenta de linha de comando %s não pôde ser encontrada", "The library %s is not available." : "A biblioteca %s não está disponível.", "Library %s with a version higher than %s is required - available version %s." : "É requerida uma biblioteca %s com uma versão maior que %s - versão disponível %s.", "Library %s with a version lower than %s is required - available version %s." : "É requerida uma biblioteca %s com uma versão menor que %s - versão disponível %s.", "Following platforms are supported: %s" : "As seguintes plataformas são suportadas: %s", "Server version %s or higher is required." : "É requerido um servidor da versão %s ou superior.", "Server version %s or lower is required." : "É requerido um servidor da versão %s ou abaixo.", "Unknown filetype" : "Tipo de arquivo desconhecido", "Invalid image" : "Imagem inválida", "Avatar image is not square" : "A imagem do avatar não é quadrada", "today" : "hoje", "yesterday" : "ontem", "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás"], "last month" : "último mês", "_%n month ago_::_%n months ago_" : ["há %n mês atrás","há %n meses atrás"], "last year" : "último ano", "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás"], "_%n hour ago_::_%n hours ago_" : ["há %n hora atrás","há %n horas atrás"], "_%n minute ago_::_%n minutes ago_" : ["há %n minuto atrás","há %n minutos atrás"], "seconds ago" : "segundos atrás", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "O módulo com a ID: %s não existe. Por favor, habilite-o nas configurações de seu aplicativo ou contacte o administrador.", "File name is a reserved word" : "O nome do arquivo é uma palavra reservada", "File name contains at least one invalid character" : "O nome do arquivo contém pelo menos um caracter inválido", "File name is too long" : "O nome do arquivo é muito longo", "Dot files are not allowed" : "Arquivos Dot não são permitidos", "Empty filename is not allowed" : "Nome vazio para arquivo não é permitido.", "App \"%s\" cannot be installed because appinfo file cannot be read." : "O aplicativo \"%s\" não pode ser instalado pois o arquivo appinfo não pôde ser lido.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "O aplicativo \"%s\" não pode ser instalado pois não é compatível com a versão do servidor.", "This is an automatically sent email, please do not reply." : "Este é um e-mail enviado automaticamente. Por favor, não responda.", "Help" : "Ajuda", "Apps" : "Aplicativos", "Settings" : "Configurações", "Log out" : "Sair", "Users" : "Usuários", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Configurações básicas", "Sharing" : "Compartilhamento", "Security" : "Segurança", "Encryption" : "Criptografia", "Additional settings" : "Configurações adicionais", "Tips & tricks" : "Dicas & truques", "Personal info" : "Informação Pessoal", "Sync clients" : "Clientes de sincronização", "Unlimited" : "Ilimitado", "__language_name__" : "__language_name__", "Verifying" : "Verificando", "Verifying …" : "Verificando...", "Verify" : "Verificar", "%s enter the database username and name." : "%s insira o nome de usuário e o nome do banco de dados.", "%s enter the database username." : "%s insira o nome de usuário do banco de dados.", "%s enter the database name." : "%s insira o nome do banco de dados.", "%s you may not use dots in the database name" : "%s você não pode usar pontos no nome do banco de dados", "Oracle connection could not be established" : "Conexão Oracle não pôde ser estabelecida", "Oracle username and/or password not valid" : "Nome de usuário e/ou senha Oracle inválidos", "PostgreSQL username and/or password not valid" : "Nome de usuário e/ou senha PostgreSQL inválidos", "You need to enter details of an existing account." : "Você necessita entrar detalhes de uma conta existente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X não é suportado e %s não funcionará corretamente nesta plataforma. Use-o por sua conta e risco!", "For the best results, please consider using a GNU/Linux server instead." : "Para obter melhores resultados, por favor considere o uso de um servidor GNU/Linux em seu lugar.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Aparentemente a instância %s está rodando em um ambiente PHP de 32 bits e o open_basedir foi configurado no php.ini. Isto pode gerar problemas com arquivos maiores que 4GB e é altamente não recomendável.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor, remova a configuração de open_basedir de seu php.ini ou mude o PHP para 64bit.", "Set an admin username." : "Defina um nome do usuário administrador.", "Set an admin password." : "Defina uma senha para o administrador.", "Can't create or write into the data directory %s" : "Não foi possível criar ou gravar no diretório de dados %s", "Invalid Federated Cloud ID" : "ID inválida de Nuvem Federada", "Sharing %s failed, because the backend does not allow shares from type %i" : "O compartilhamento %s falhou pois a plataforma de serviço não permite ações de tipo %i", "Sharing %s failed, because the file does not exist" : "Compartilhamento %s falhou pois o arquivo não existe", "You are not allowed to share %s" : "Você não tem permissão para compartilhar %s", "Sharing %s failed, because you can not share with yourself" : "O compartilhamento %s falhou pois você não pode compartilhar com você mesmo", "Sharing %s failed, because the user %s does not exist" : "O compartilhamento %s falhou pois o usuário %s não existe", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "O compartilhamento %s falhou pois o usuário %s não é membro de nenhum grupo que o usuário %s pertença", "Sharing %s failed, because this item is already shared with %s" : "O compartilhamento %s falhou pois este ítem já está compartilhado com %s", "Sharing %s failed, because this item is already shared with user %s" : "O compartilhamento de %s falhou pois esse item já é compartilhada com o usuário %s", "Sharing %s failed, because the group %s does not exist" : "O compartilhamento %s falhou pois o grupo %s não existe", "Sharing %s failed, because %s is not a member of the group %s" : "O compartilhamento %s falhou, pois %s não é membro do grupo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Você precisa fornecer uma senha para criar um link público, apenas links protegidos são permitidos", "Sharing %s failed, because sharing with links is not allowed" : "O compartilhamento %s falhou pois compartilhamento com links não é permitido", "Not allowed to create a federated share with the same user" : "Não é permitido criar um compartilhamento associado com o mesmo usuário", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "O compartilhamento %s falhou pois não foi possível encontrar %s. Talvez o servidor esteja inacessível.", "Share type %s is not valid for %s" : "O tipo de compartilhamento %s não é válido para %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Não foi possível definir a data de expiração. Os compartilhamentos não podem expirar mais tarde que %s depois de terem sido compartilhados", "Cannot set expiration date. Expiration date is in the past" : "Não foi possível definir a data de expiração pois ela está no passado", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "A plataforma de compartilhamento %s deve implementar a interface OCP\\Share_Backend", "Sharing backend %s not found" : "Plataforma de serviço de compartilhamento %s não encontrada", "Sharing backend for %s not found" : "Plataforma de compartilhamento para %s não foi encontrada", "Sharing failed, because the user %s is the original sharer" : "O compartilhamento falhou pois o usuário %s é o compartilhador original", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Compartilhamento %s falhou pois as permissões excedem as permissões concedidas a %s", "Sharing %s failed, because resharing is not allowed" : "O compartilhamento %s falhou pois recompartilhamentos não são permitidos", "Sharing %s failed, because the sharing backend for %s could not find its source" : "O compartilhamento %s falhou pois a plataforma de serviço de compartilhamento para %s não conseguiu encontrar a sua fonte", "Sharing %s failed, because the file could not be found in the file cache" : "O compartilhamento %s falhou pois o arquivo não pôde ser encontrado no cache de arquivos", "Can’t increase permissions of %s" : "Não posso aumentar as permissões de %s", "Files can’t be shared with delete permissions" : "Os arquivos não podem ser compartilhados com permissões de exclusão", "Files can’t be shared with create permissions" : "Os arquivos não podem ser compartilhados com permissões de criação", "Expiration date is in the past" : "Data de expiração está no passado", "Can’t set expiration date more than %s days in the future" : "Não é possível definir a expiração mais do que %s dias no futuro", "%s shared »%s« with you" : "%s compartilhou »%s« com você", "%s shared »%s« with you." : "%s compartilhou »%s« com você.", "Click the button below to open it." : "Clique no botão abaixo para abri-lo.", "Open »%s«" : "Abrir »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "O compartilhamento solicitado não existe mais", "Could not find category \"%s\"" : "Impossível localizar a categoria \"%s\"", "Sunday" : "Domingo", "Monday" : "Segunda-feira", "Tuesday" : "Terça-feira", "Wednesday" : "Quarta-feira", "Thursday" : "Quinta-feira", "Friday" : "Sexta-feira", "Saturday" : "Sábado", "Sun." : "Dom.", "Mon." : "Seg.", "Tue." : "Ter.", "Wed." : "Qua.", "Thu." : "Qui.", "Fri." : "Sex.", "Sat." : "Sab.", "Su" : "Su", "Mo" : "Se", "Tu" : "Te", "We" : "Qu", "Th" : "Qu", "Fr" : "Se", "Sa" : "Sa", "January" : "Janeiro", "February" : "Fevereiro", "March" : "Março", "April" : "Abril", "May" : "Maio", "June" : "Junho", "July" : "Julho", "August" : "Agosto", "September" : "Setembro", "October" : "Outubro", "November" : "Novembro", "December" : "Dezembro", "Jan." : "Jan.", "Feb." : "Fev.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "Mai.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Ago.", "Sep." : "Set.", "Oct." : "Out.", "Nov." : "Nov.", "Dec." : "Dez.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Somente os seguintes caracteres são permitidos em um nome de usuário: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-'\"", "A valid username must be provided" : "Um nome de usuário válido deve ser fornecido", "Username contains whitespace at the beginning or at the end" : "O nome de usuário contém espaço em branco no início ou no fim", "Username must not consist of dots only" : "Nome do usuário não pode consistir de pontos somente", "A valid password must be provided" : "Uma senha válida deve ser fornecida", "The username is already being used" : "Este nome de usuário já está em uso", "Could not create user" : "Não foi possível criar o usuário", "User disabled" : "Usuário desativado", "Login canceled by app" : "Login cancelado pelo aplicativo", "No app name specified" : "O nome do aplicativo não foi especificado.", "App '%s' could not be installed!" : "O aplicativo '%s' não pôde ser instalado!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "O aplicativo \"%s\" não pode ser instalado pois as seguintes dependências não foram cumpridas: %s", "a safe home for all your data" : "Um lar seguro para todos os seus dados", "File is currently busy, please try again later" : "O arquivo está ocupado, tente novamente mais tarde", "Can't read file" : "Não foi possível ler arquivo", "Application is not enabled" : "O aplicativo não está habilitado", "Authentication error" : "Erro de autenticação", "Token expired. Please reload page." : "O token expirou. Por favor recarregue a página.", "Unknown user" : "Usuário desconhecido", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nenhum driver de banco de dados (sqlite, mysql ou postgresql) instalado.", "Cannot write into \"config\" directory" : "Não foi possível gravar no diretório \"config\"", "Cannot write into \"apps\" directory" : "Não foi possível gravar no diretório \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Normalmente isso pode ser resolvido dando ao webserver permissão de escrita no diretório apps ou desabilitando a appstore no arquivo de configuração. Veja %s", "Cannot create \"data\" directory" : "Não foi possível criar o diretório \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Normalmente isso pode ser resolvido dando ao webserver permissão de escrita no diretório raiz. Veja %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "As permissões normalmente podem ser corrigidas dando permissão de escrita do diretório raiz para o servidor web. Veja %s.", "Setting locale to %s failed" : "Falha ao configurar localização para %s", "Please install one of these locales on your system and restart your webserver." : "Por favor, defina uma dessas localizações em seu sistema e reinicie o seu servidor web.", "Please ask your server administrator to install the module." : "Por favor, peça ao seu administrador do servidor para instalar o módulo.", "PHP module %s not installed." : "Módulo PHP %s não instalado.", "PHP setting \"%s\" is not set to \"%s\"." : "Configuração PHP \"%s\" não está configurado para \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ajustar a configuração no php.ini fará com que o Nextcloud execute novamente", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload está definido para \"%s\" ao invés do valor esperado \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Para corrigir esse problema defina <code>mbstring.func_overload</code> para <code>0</code> em seu php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "A libxml2 2.7.0 é a versão mínima requerida. Atualmente a versão %s está instalada.", "To fix this issue update your libxml2 version and restart your web server." : "Para corrigir este problema, atualize a versão da sua libxml2 e reinicie seu servidor web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP aparentemente está configurado para retirar blocos doc inline. Isso fará com que vários aplicativos do núcleo fiquem inacessíveis.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Isso provavelmente é causado por um cache/acelerador, como Zend OPcache ou eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Módulos do PHP foram instalados, mas eles ainda estão listados como faltantes?", "Please ask your server administrator to restart the web server." : "Por favor peça ao administrador do servidor para reiniciar o servidor web.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 requirido", "Please upgrade your database version" : "Por favor atualize sua versão do banco de dados", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor altere as permissões para 0770 para que o diretório não possa ser lido por outros usuários.", "Your data directory is readable by other users" : "O diretório de dados está legível para outros usuários", "Your data directory must be an absolute path" : "O diretório de dados deve ser um caminho absoluto", "Check the value of \"datadirectory\" in your configuration" : "Verifique o valor do \"datadirectory\" na sua configuração", "Your data directory is invalid" : "Seu diretório de dados é inválido", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Assegure-se que exista um arquivo chamado \".ocdata\" na raiz do diretório \"data\".", "Could not obtain lock type %d on \"%s\"." : "Não foi possível obter tipo de bloqueio %d em \"%s\".", "Storage unauthorized. %s" : "Armazenamento não autorizado. %s", "Storage incomplete configuration. %s" : "Configuração incompleta do armazenamento. %s", "Storage connection error. %s" : "Erro na conexão de armazenamento. %s", "Storage is temporarily not available" : "Armazenamento temporariamente indisponível", "Storage connection timeout. %s" : "Esgotado o tempo de conexão ao armazenamento. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Isso geralmente pode ser corrigido por %s dar a permissão de gravação ao servidor web para o diretório de configuração %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "O módulo com ID: %s não existe. Ative-o em suas configurações de aplicativos ou contacte o administrador.", "Server settings" : "Configurações do servidor", "DB Error: \"%s\"" : "Erro no BD: \"%s\"", "Offending command was: \"%s\"" : "Comando ofensivo era: \"%s\"", "You need to enter either an existing account or the administrator." : "Você precisa inserir uma conta existente ou a conta do administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando ofensivo era: \"%s\", nome: %s, senha: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "A definição de permissões para %s falhou pois as permissões excedem as permissões concedidas a %s", "Setting permissions for %s failed, because the item was not found" : "A definição de permissões para %s falhou pois o item não foi encontrado", "Cannot clear expiration date. Shares are required to have an expiration date." : "Não foi possível eliminar a data de expiração. Compartilhamentos devem ter uma data de expiração.", "Cannot increase permissions of %s" : "Não foi possível aumentar as permissões de %s", "Files can't be shared with delete permissions" : "Os arquivos não podem ser compartilhadas com permissões de exclusão", "Files can't be shared with create permissions" : "Os arquivos não podem ser compartilhados com permissões de criação", "Cannot set expiration date more than %s days in the future" : "Não foi possível definir a data de expiração para mais que %s dias no futuro", "Personal" : "Pessoal", "Admin" : "Admininistrador", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Isto pode ser corrigido por %sdando ao servidor web permissão de escrita para o diretório app%s ou desabilitando o appstore no arquivo de configuração.", "Cannot create \"data\" directory (%s)" : "Não pôde ser criado o diretório \"dados\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Isto geralmente pode ser corrigido ao <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">dar permissão de gravação no diretório raiz</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Permissões podem ser corrigidas por %sdando permissão de escrita ao servidor web para o diretório raiz %s", "Data directory (%s) is readable by other users" : "Diretório de dados (%s) pode ser lido por outros usuários", "Data directory (%s) must be an absolute path" : "Diretório de dados (%s) deve ser um caminho absoluto", "Data directory (%s) is invalid" : "Diretório de dados (%s) é inválido", "Please check that the data directory contains a file \".ocdata\" in its root." : "Por favor, verifique se o diretório de dados contém um arquivo \".ocdata\" em sua raiz." }, "nplurals=2; plural=(n > 1);"); l10n/es_AR.json 0000604 00000054615 15247130447 0007220 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "¡No se puede escribir en el directorio \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Esto generalmente se soluciona dándole al servidor web acceso para escribir en el directorio config. ", "See %s" : "Ver %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio config. Favor de ver %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Los archivos de la aplicación %$1s no fueron correctamente remplazados. Favor de asegurarse de que la versión sea compatible con el servidor.", "Sample configuration detected" : "Se ha detectado la configuración de muestra", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Se ha detectado que la configuración de muestra ha sido copiada. Esto puede descomponer su instalacón y no está soportado. Favor de leer la documentación antes de hacer cambios en el archivo config.php", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s y %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s y %5$s", "Enterprise bundle" : "Paquete empresarial", "Groupware bundle" : "Paquete de Groupware", "Social sharing bundle" : "Paquete para compartir en redes sociales", "PHP %s or higher is required." : "Se requiere de PHPH %s o superior.", "PHP with a version lower than %s is required." : "PHP con una versión inferiror a la %s es requerido. ", "%sbit or higher PHP required." : "se requiere PHP para %sbit o superior.", "Following databases are supported: %s" : "Las siguientes bases de datos están soportadas: %s", "The command line tool %s could not be found" : "No fue posible encontar la herramienta de línea de comando %s", "The library %s is not available." : "La biblioteca %s no está disponible. ", "Library %s with a version higher than %s is required - available version %s." : "La biblitoteca %s con una versión superiror a la %s es requerida - versión disponible %s.", "Library %s with a version lower than %s is required - available version %s." : "Se requiere de la biblioteca %s con una versión inferiror a la %s - la versión %s está disponible. ", "Following platforms are supported: %s" : "Las siguientes plataformas están soportadas: %s", "Server version %s or higher is required." : "Se requiere la versión del servidor %s o superior. ", "Server version %s or lower is required." : "La versión del servidor %s o inferior es requerdia. ", "Unknown filetype" : "Tipo de archivo desconocido", "Invalid image" : "Imagen inválida", "Avatar image is not square" : "La imagen del avatar no es un cuadrado", "today" : "hoy", "yesterday" : "ayer", "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"], "last month" : "mes pasado", "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"], "last year" : "año pasado", "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"], "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"], "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"], "seconds ago" : "hace segundos", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Favor de habilitarlo en sus configuraciones de aplicación o contacte a su administrador. ", "File name is a reserved word" : "Nombre de archivo es una palabra reservada", "File name contains at least one invalid character" : "El nombre del archivo contiene al menos un caracter inválido", "File name is too long" : "El nombre del archivo es demasiado largo", "Dot files are not allowed" : "Los archivos Dot no están permitidos", "Empty filename is not allowed" : "El uso de nombres de archivo vacíos no está permitido", "App \"%s\" cannot be installed because appinfo file cannot be read." : "La aplicación \"%s\" no puede ser instalada porque el archivo appinfo no se puede leer. ", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "La aplicación \"%s\" no puede ser instalada porque no es compatible con esta versión del servidor. ", "This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, favor de no contestarlo. ", "Help" : "Ayuda", "Apps" : "Aplicaciones", "Log out" : "Cerrar sesión", "Users" : "Usuarios", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Configuraciones básicas", "Sharing" : "Compartiendo", "Security" : "Seguridad", "Encryption" : "Encripción", "Additional settings" : "Configuraciones adicionales", "Tips & tricks" : "Consejos y trucos", "%s enter the database username and name." : "%s ingrese el nombre del usuario y nombre de la base de datos", "%s enter the database username." : "%s ingresar el nombre de usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s no puede utilizar puntos en el nombre de la base de datos", "Oracle connection could not be established" : "No fue posible establecer la conexión a Oracle", "Oracle username and/or password not valid" : "El nombre de usuario y/o contraseña de Oracle inválidos", "PostgreSQL username and/or password not valid" : "El nombre de usuario y/o contraseña de PostgreSQL inválidos", "You need to enter details of an existing account." : "Necesita ingresar los detalles de una cuenta existente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "OS X de Mac no está soportado y %s no funcionará correctamente en esta plataforma ¡Uselo bajo su propio riesgo!", "For the best results, please consider using a GNU/Linux server instead." : "Para mejores resultados, favor de cosiderar usar en su lugar un servidor GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Al parecer esta instancia %s está corriendo en un ambiente PHP de 32-bits y el open_basedir ha sido configurado en el archivo php.ini. Esto generará problemas con archivos de más de 4GB de tamaño y es altamente desalentado. ", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Favor de eliminar el ajuste open_basedir de su archivo php.ini o cambie a PHP de 64 bits. ", "Set an admin username." : "Configurar un nombre de usuario del administrador", "Set an admin password." : "Establecer la contraseña del administrador.", "Can't create or write into the data directory %s" : "No es posible crear o escribir en el directorio de datos %s", "Invalid Federated Cloud ID" : "ID de Nube Federada Inválido", "Sharing %s failed, because the backend does not allow shares from type %i" : "Se presentó una falla al compartir %s, porque el backend no permite elementos compartidos de tipo %i", "Sharing %s failed, because the file does not exist" : "Se presentó una falla al compartir %s porque el archivo no existe", "You are not allowed to share %s" : "No tiene permitido compartir %s", "Sharing %s failed, because you can not share with yourself" : "Se presento una falla al compartir %s, porque no puede compartir con usted mismo", "Sharing %s failed, because the user %s does not exist" : "Se presentó una falla al compartir %s porque el usuario %s no existe", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Se presentó una falla al compartir %s proque el usuario %s no es un miembro de ninguno de los grupos de los cuales %s es miembro", "Sharing %s failed, because this item is already shared with %s" : "Se presento una falla al compartir %s, porque este elemento ya ha sido compartido con %s", "Sharing %s failed, because this item is already shared with user %s" : "Se presento una falla al compartir %s, porque este elemento ya ha sido compartido con el usuario %s", "Sharing %s failed, because the group %s does not exist" : "Se presentó una falla al compartir %s, porque el grupo %s no existe", "Sharing %s failed, because %s is not a member of the group %s" : "Se presentó una falla al compartir %s debido a que %s no es un miembro del grupo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Usted necesita proporcionar una contraseña para crear un link público, sólo los links protegidos están permitidos. ", "Sharing %s failed, because sharing with links is not allowed" : "Se presentó una falla al compartir %s porque no está permitido compartir con links", "Not allowed to create a federated share with the same user" : "No está permitido crear un elemento compartido federado con el mismo usuario", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Se presentó una falla al compartir %s, no fue posible encontrar %s, tal vez el servidor sea inalcanzable por el momento", "Share type %s is not valid for %s" : "El tipo del elemento compartido %s no es válido para %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "No ha sido posible establecer la fecha de expiración. Los recursos compartidos no pueden expirar después de %s tras haber sido compartidos", "Cannot set expiration date. Expiration date is in the past" : "No ha sido posible establecer la fecha de expiración. La fecha de expiración ya ha pasado", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El backend %s que comparte debe implementar la interface OCP\\Share_Backend", "Sharing backend %s not found" : "No fue encontrado el Backend que comparte %s ", "Sharing backend for %s not found" : "No fue encontrado el Backend que comparte para %s", "Sharing failed, because the user %s is the original sharer" : "Se presento una falla al compartir, porque el usuario %s es quien compartió originalmente", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Se presentó una falla al compartir %s, porque los permisos exceden los permisos otorgados a %s", "Sharing %s failed, because resharing is not allowed" : "Falla al compartir %s debído a que no se permite volver a compartir", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Se presentó una falla al compartir %s porque el backend que comparte %s no pudo encontrar su origen", "Sharing %s failed, because the file could not be found in the file cache" : "Se presentó una falla al compartir %s porque el archivo no se encontró en el caché de archivos", "Expiration date is in the past" : "La fecha de expiración ya ha pasado", "%s shared »%s« with you" : "%s ha compartido »%s« con usted", "%s via %s" : "%s por %s", "Could not find category \"%s\"" : "No fue posible encontrar la categoria \"%s\"", "Sunday" : "Domingo", "Monday" : "Lunes", "Tuesday" : "Martes", "Wednesday" : "Miércoles", "Thursday" : "Jueves", "Friday" : "Viernes", "Saturday" : "Sábado", "Sun." : "Dom.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mie.", "Thu." : "Jue.", "Fri." : "Vie.", "Sat." : "Sab.", "Su" : "Do", "Mo" : "Lu", "Tu" : "Ma", "We" : "Mi", "Th" : "Ju", "Fr" : "Vi", "Sa" : "Sa", "January" : "Enero", "February" : "Febrero", "March" : "Marzo", "April" : "Abril", "May" : "Mayo", "June" : "Junio", "July" : "Julio", "August" : "Agosto", "September" : "Septiembre", "October" : "Octubre", "November" : "Noviembre", "December" : "Diciembre", "Jan." : "Ene.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "May.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Ago.", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Dic.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Sólo se permiten los siguientes caracteres en el nombre de usuario: \"a-z\", \"A-Z\", \"0-9\" y \"_.@-'\"", "A valid username must be provided" : "Se debe proporcionar un nombre de usuario válido", "Username contains whitespace at the beginning or at the end" : "El nombre del usuario contiene un espacio en blanco al inicio o al final", "Username must not consist of dots only" : "El nombre de usuario no debe consistir de solo puntos. ", "A valid password must be provided" : "Se debe proporcionar una contraseña válida", "The username is already being used" : "Ese nombre de usuario ya está en uso", "User disabled" : "Usuario deshabilitado", "Login canceled by app" : "Inicio de sesión cancelado por la aplicación", "No app name specified" : "No se ha especificado el nombre de la aplicación", "App '%s' could not be installed!" : "¡La aplicación \"%s\" no puede ser instalada!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "La aplicación \"%s\" no puede ser instalada porque las siguientes dependencias no están satisfechas: %s ", "a safe home for all your data" : "un lugar seguro para todos sus datos", "File is currently busy, please try again later" : "El archivo se encuentra actualmente en uso, favor de intentarlo más tarde. ", "Can't read file" : "No se puede leer el archivo", "Application is not enabled" : "La aplicación está deshabilitada", "Authentication error" : "Error de autenticación", "Token expired. Please reload page." : "La ficha ha expirado. Favor de recarga la página.", "Unknown user" : "Ususario desconocido", "No database drivers (sqlite, mysql, or postgresql) installed." : "No cuenta con controladores de base de datos (sqlite, mysql o postgresql) instalados. ", "Cannot write into \"config\" directory" : "No fue posible escribir en el directorio \"config\"", "Cannot write into \"apps\" directory" : "No fue posible escribir en el directorio \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio de las aplicaciones o deshabilitando la appstore en el archivo config. Favor de ver %s", "Cannot create \"data\" directory" : "No fue posible crear el directorio \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio raíz. Favor de ver %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Por lo general los permisos se pueden corregir al darle al servidor web acceso de escritura al directorio raíz. Favor de ver %s.", "Setting locale to %s failed" : "Se presentó una falla al establecer la regionalización a %s", "Please install one of these locales on your system and restart your webserver." : "Favor de instalar uno de las siguientes configuraciones locales en su sistema y reinicie su servidor web", "Please ask your server administrator to install the module." : "Favor de solicitar a su adminsitrador la instalación del módulo. ", "PHP module %s not installed." : "El módulo de PHP %s no está instalado. ", "PHP setting \"%s\" is not set to \"%s\"." : "El ajuste PHP \"%s\" no esta establecido a \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "El cambiar este ajuste del archivo php.ini hará que Nextcloud corra de nuevo.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload está establecido como \"%s\" en lugar del valor esperado de \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Para corregir este tema, establezca <code>mbstring.func_overload</code> a <code>0</code> en su archivo php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Se requiere de por lo menos libxml2 2.7.0. Actualmente %s esta instalado. ", "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este tema, favor de actualizar la versión de su libxml2 y reinicie su servidor web. ", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Al parecer PHP está configurado para quitar los bloques de comentarios internos. Esto hará que varias aplicaciones principales sean inaccesibles. ", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Esto ha sido causado probablemente por un acelerador de caché como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "¿Los módulos de PHP han sido instalados, pero se siguen enlistando como faltantes?", "Please ask your server administrator to restart the web server." : "Favor de solicitar al administrador reiniciar el servidor web. ", "PostgreSQL >= 9 required" : "Se requiere PostgreSQL >= 9", "Please upgrade your database version" : "Favor de actualizar la versión de la base de datos", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Favor de cambiar los permisos a 0770 para que el directorio no pueda ser enlistado por otros usuarios. ", "Your data directory is readable by other users" : "Su direcctorio data puede ser leído por otros usuarios", "Your data directory must be an absolute path" : "Su direcctorio data debe ser una ruta absoluta", "Check the value of \"datadirectory\" in your configuration" : "Verifique el valor de \"datadirectory\" en su configuración", "Your data directory is invalid" : "Su directorio de datos es inválido", "Could not obtain lock type %d on \"%s\"." : "No fue posible obtener el tipo de bloqueo %d en \"%s\". ", "Storage unauthorized. %s" : "Almacenamiento no autorizado. %s", "Storage incomplete configuration. %s" : "Configuración incompleta del almacenamiento. %s", "Storage connection error. %s" : "Se presentó un error con la conexión al almacenamiento. %s", "Storage is temporarily not available" : "El almacenamieto se encuentra temporalmente no disponible", "Storage connection timeout. %s" : "Se agotó el tiempo de conexión del almacenamiento. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Esto generalmente se soluciona %s dándole al servidor web acceso para escribir en el directorio config %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con id: %s no existe. Favor de habilitarlo en sus configuraciones de aplicación o contacte a su administrador. ", "Server settings" : "Configuraciones del servidor", "DB Error: \"%s\"" : "Error de BD: \"%s\"", "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", "You need to enter either an existing account or the administrator." : "Necesita ingresar una cuenta ya sea existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando infractor: \"%s\", nombre: %s, contraseña: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Se persentó una falla al establecer los permisos para %s, porque los permisos exceden los permisos otorgados a %s", "Setting permissions for %s failed, because the item was not found" : "Se persentó una falla al establecer los permisos para %s, porque no se encontró el elemento ", "Cannot clear expiration date. Shares are required to have an expiration date." : "No ha sido posible borrar la fecha de expiración. Los elelentos compartidos deben tener una fecha de expiración.", "Cannot increase permissions of %s" : "No se pueden incrementar los permisos de %s", "Files can't be shared with delete permissions" : "No es posible compartir archivos con permisos de borrado", "Files can't be shared with create permissions" : "No es posible compartir archivos con permisos de creación", "Cannot set expiration date more than %s days in the future" : "No es posible establecer la fecha de expiración más allá de %s días en el futuro", "Personal" : "Personal", "Admin" : "Administración", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Esto se puede arreglar por %s al darle acceso de escritura al servidor web al directorio de las aplicaciones %s o al deshabilitar la tienda de aplicaciones en el archivo de configuración", "Cannot create \"data\" directory (%s)" : "No fue posible crear el directorio (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Esto se puede arreglar generalmente al <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">darle al servidor web accesos al directorio raíz</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Los permisos se pueden arreglar generalmente al %s darle al servidor web accesos al direcotiro raíz %s.", "Data directory (%s) is readable by other users" : "El directorio de datos (%s) puede ser leído por otros usuarios", "Data directory (%s) must be an absolute path" : "El directorio de datos (%s) debe ser una ruta absoluta", "Data directory (%s) is invalid" : "El directorio de datos (%s) es inválido", "Please check that the data directory contains a file \".ocdata\" in its root." : "Favor de verificar que el directorio de datos tenga un archivo \".ocdata\" en su raíz. " },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/el.js 0000604 00000077613 15247130447 0006275 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Αδυναμία εγγραφής στον κατάλογο \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Αυτό μπορεί συνήθως να διορθωθεί παρέχοντας δικαιώματα εγγραφής για το φάκελο config στο διακομιστή δικτύου", "See %s" : "Δείτε %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Αυτό μπορεί συνήθως να διορθωθεί δίνοντας στον διακομιστή γραπτή πρόσβαση στον κατάλογο εκχώρησης. Βλέπε%s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Τα αρχεία της εφαρμογής% $ 1s δεν αντικαταστάθηκαν σωστά. Βεβαιωθείτε ότι πρόκειται για μια έκδοση που είναι συμβατή με το διακομιστή.", "Sample configuration detected" : "Ανιχνεύθηκε δείγμα εγκατάστασης", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Έχει ανιχνευθεί ότι το δείγμα εγκατάστασης έχει αντιγραφεί. Αυτό μπορεί να σπάσει την εγκατάστασή σας και δεν υποστηρίζεται. Παρακαλώ διαβάστε την τεκμηρίωση πριν εκτελέσετε αλλαγές στο config.php", "%1$s and %2$s" : "%1$s και %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s και %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s και %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s και %5$s", "Education Edition" : "Εκπαιδευτική Έκδοση", "Enterprise bundle" : "Πακέτο επιχειρήσεων", "Groupware bundle" : "Ομάδα δέσμης", "Social sharing bundle" : "Πακέτο κοινωνικού διαμοιρασμού", "PHP %s or higher is required." : "PHP %s ή νεώτερη απαιτείται.", "PHP with a version lower than %s is required." : "Απαιτείται PHP παλαιότερη από την έκδοση %s.", "%sbit or higher PHP required." : "%sbit απαιτείται νεώτερη έκδοση PHP.", "Following databases are supported: %s" : " Υποστηρίζονται οι ακόλουθες βάσεις δεδομένων: %s", "The command line tool %s could not be found" : "Το εργαλείο γραμμής εντολών %s δεν μπορεί να βρεθεί", "The library %s is not available." : "Το %s της βιβλιοθήκης δεν είναι διαθέσιμο.", "Library %s with a version higher than %s is required - available version %s." : "Απαιτείται βιβλιοθήκη %s νεότερη από την έκδοση %s - διαθέσιμη έκδοση %s ", "Library %s with a version lower than %s is required - available version %s." : "Απαιτείται βιβλιοθήκη %s παλαιότερη από την έκδοση %s - διαθέσιμη έκδοση %s ", "Following platforms are supported: %s" : "Οι ακόλουθες πλατφόρμες υποστηρίζονται: %s", "Server version %s or higher is required." : "Απαιτείται έκδοση διακομιστή %s ή νεότερη.", "Server version %s or lower is required." : "Απαιτείται έκδοση διακομιστή %s ή παλαιότερη.", "Unknown filetype" : "Άγνωστος τύπος αρχείου", "Invalid image" : "Μη έγκυρη εικόνα", "Avatar image is not square" : "Η εικόνα του άβαταρ δεν είναι τετράγωνη", "today" : "σήμερα", "yesterday" : "χτες", "_%n day ago_::_%n days ago_" : ["%n ημέρα πριν","%n ημέρες πριν"], "last month" : "τελευταίο μήνα", "_%n month ago_::_%n months ago_" : ["πριν %n μήνα","πριν %n μήνες"], "last year" : "τελευταίο χρόνο", "_%n year ago_::_%n years ago_" : ["%n χρόνο πριν","%n χρόνια πριν"], "_%n hour ago_::_%n hours ago_" : ["%nώρα πριν","%nώρες πριν"], "_%n minute ago_::_%n minutes ago_" : ["%nλεπτό πριν","%nλεπτά πριν"], "seconds ago" : "δευτερόλεπτα πριν", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Το άρθρωμα με ID: %sδεν υπάρχει. Παρακαλούμε ενεργοποιήστε το στις ρυθμίσεις των εφαρμογών σας ή επικοινωνήστε με τον διαχειριστή.", "File name is a reserved word" : "Το όνομα αρχείου είναι λέξη που έχει δεσμευτεί", "File name contains at least one invalid character" : "Το όνομα αρχείου περιέχει έναν τουλάχιστον μη έγκυρο χαρακτήρα", "File name is too long" : "Το όνομα αρχείου είνια πολύ μεγάλο", "Dot files are not allowed" : "Δεν επιτρέπονται αρχεία που ξεκινούν από τελεία - Dot ", "Empty filename is not allowed" : "Δεν επιτρέπεται άδειο όνομα αρχείου", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Η εφαρμογή \"%s\" δεν μπορεί να εγκατασταθεί διότι δεν είναι δυνατή η ανάγνωση του αρχείου appinfo.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Η εφαρμογή \"%s\" δεν μπορεί να εγκατασταθεί διότι δεν είναι συμβατή με την έκδοση του διακομιστή.", "This is an automatically sent email, please do not reply." : "Αυτό είναι ένα μήνυμα ηλεκτρονικού ταχυδρομείου που στάλθηκε αυτόματα, παρακαλούμε μην απαντήσετε.", "Help" : "Βοήθεια", "Apps" : "Εφαρμογές", "Settings" : "Ρυθμίσεις", "Log out" : "Έξοδος", "Users" : "Χρήστες", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Βασικές ρυθμίσεις", "Sharing" : "Διαμοιρασμός", "Security" : "Ασφάλεια", "Encryption" : "Κρυπτογράφηση", "Additional settings" : "Επιπρόσθετες ρυθμίσεις", "Tips & tricks" : "Συμβουλές & κόλπα", "Personal info" : "Προσωπικές πληροφορίες", "Sync clients" : "Εφαρμογές συγχρονισμού", "Unlimited" : "Απεριόριστα", "__language_name__" : "__language_name__", "Verifying" : "Γίνεται επαλήθευση", "Verifying …" : "Γίνεται επαλήθευση ...", "Verify" : "Επαλήθευση", "%s enter the database username and name." : "%sπληκτρολογήστε όνομα χρήστη και όνομα βάσης δεδομένων.", "%s enter the database username." : "%s εισάγετε το όνομα χρήστη της βάσης δεδομένων.", "%s enter the database name." : "%s εισάγετε το όνομα της βάσης δεδομένων.", "%s you may not use dots in the database name" : "%s μάλλον δεν χρησιμοποιείτε τελείες στο όνομα της βάσης δεδομένων", "Oracle connection could not be established" : "Αδυναμία σύνδεσης Oracle", "Oracle username and/or password not valid" : "Μη έγκυρος χρήστης και/ή συνθηματικό της Oracle", "PostgreSQL username and/or password not valid" : "Μη έγκυρος χρήστης και/ή συνθηματικό της PostgreSQL", "You need to enter details of an existing account." : "Χρειάζεται να εισάγετε λεπτομέρειες από υπάρχον λογαριασμό.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Το Mac OS X δεν υποστηρίζεται και το %s δεν θα λειτουργήσει σωστά σε αυτή την πλατφόρμα. Χρησιμοποιείτε με δική σας ευθύνη!", "For the best results, please consider using a GNU/Linux server instead." : "Για καλύτερα αποτελέσματα, παρακαλούμε εξετάστε την μετατροπή σε έναν διακομιστή GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Φαίνεται ότι η εγκατάσταση %s εκτελείται σε περιβάλλον 32-bit PHP και η επιλογη open_basedir έχει ρυθμιστεί στο αρχείο php.ini. Αυτό θα οδηγήσει σε προβλήματα με αρχεία πάνω από 4 GB και δεν συνίσταται.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Παρακαλώ αφαιρέστε την ρύθμιση open_basedir μέσα στο αρχείο php.ini ή αλλάξτε σε 64-bit PHP.", "Set an admin username." : "Εισάγετε όνομα χρήστη διαχειριστή.", "Set an admin password." : "Εισάγετε συνθηματικό διαχειριστή.", "Can't create or write into the data directory %s" : "Αδύνατη η δημιουργία ή συγγραφή στον κατάλογο δεδομένων %s", "Invalid Federated Cloud ID" : "Μη έγκυρο Federated Cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Αποτυχία διαμοιρασμού %s, γιατί το σύστημα υποστήριξης δεν επιτρέπει κοινόχρηστα τύπου %i", "Sharing %s failed, because the file does not exist" : "Ο διαμοιρασμός του %s απέτυχε, γιατί το αρχείο δεν υπάρχει", "You are not allowed to share %s" : "Δεν επιτρέπεται να διαμοιράσετε τον πόρο %s", "Sharing %s failed, because you can not share with yourself" : "Ο διαμοιρασμός του %s απέτυχε, γιατί δεν μπορείτε να διαμοιραστείτε με τον εαυτό σας.", "Sharing %s failed, because the user %s does not exist" : "Ο διαμοιρασμός του %s απέτυχε, γιατί ο χρήστης %s δεν υπάρχει", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Ο διαμοιρασμός του %s απέτυχε, γιατί ο χρήστης %s δεν είναι μέλος καμίας ομάδας στην οποία ο χρήστης %s είναι μέλος", "Sharing %s failed, because this item is already shared with %s" : "Ο διαμοιρασμός του %s απέτυχε, γιατί το αντικείμενο είναι διαμοιρασμένο ήδη με τον χρήστη %s", "Sharing %s failed, because this item is already shared with user %s" : "Αποτυχία διαμοιρασμού με %s, διότι αυτό το αντικείμενο διαμοιράζεται ήδη με τον χρήστη %s", "Sharing %s failed, because the group %s does not exist" : "Ο διαμοιρασμός του %s απέτυχε, γιατί η ομάδα χρηστών %s δεν υπάρχει", "Sharing %s failed, because %s is not a member of the group %s" : "Ο διαμοιρασμός του %s απέτυχε, γιατί ο χρήστης %s δεν είναι μέλος της ομάδας %s", "You need to provide a password to create a public link, only protected links are allowed" : "Πρέπει να εισάγετε έναν κωδικό για να δημιουργήσετε έναν δημόσιο σύνδεσμο. Μόνο προστατευμένοι σύνδεσμοι επιτρέπονται", "Sharing %s failed, because sharing with links is not allowed" : "Ο διαμοιρασμός του %s απέτυχε, γιατί δεν επιτρέπεται ο διαμοιρασμός με συνδέσμους", "Not allowed to create a federated share with the same user" : "Δεν επιτρέπεται η δημιουργία federated διαμοιρασμού με τον ίδιο χρήστη", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Αποτυχία διαμοιρασμού %s, δεν βρέθηκε το %s, μπορεί ο διακομιστής να είναι προσωρινά απροσπέλαστος.", "Share type %s is not valid for %s" : "Ο τύπος διαμοιρασμού %s δεν είναι έγκυρος για το %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Δεν μπορεί να οριστεί ημερομηνία λήξης. Οι κοινοποιήσεις δεν μπορεί να λήγουν αργότερα από %s αφού έχουν διαμοιραστεί.", "Cannot set expiration date. Expiration date is in the past" : "Δεν μπορεί να οριστεί ημερομηνία λήξης. Η ημερομηνία λήξης είναι στο παρελθόν", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Το σύστημα διαμοιρασμού %s πρέπει να υλοποιεί την διεπαφή OCP\\Share_Backend", "Sharing backend %s not found" : "Το σύστημα διαμοιρασμού %s δεν βρέθηκε", "Sharing backend for %s not found" : "Το σύστημα διαμοιρασμού για το %s δεν βρέθηκε", "Sharing failed, because the user %s is the original sharer" : "Ο διαμοιρασμός του %s απέτυχε, γιατί το αντικείμενο είναι διαμοιρασμένο αρχικά από τον ίδιο χρήστη.", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Ο διαμοιρασμός του %s απέτυχε, γιατί τα δικαιώματα υπερτερούν αυτά που είναι ορισμένα για το %s", "Sharing %s failed, because resharing is not allowed" : "Ο διαμοιρασμός του %s απέτυχε, γιατί δεν επιτρέπεται ο επαναδιαμοιρασμός", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Ο διαμοιρασμός του %s απέτυχε, γιατί δεν ήταν δυνατό να εντοπίσει την πηγή το σύστημα διαμοιρασμού για το %s ", "Sharing %s failed, because the file could not be found in the file cache" : "Ο διαμοιρασμός του %s απέτυχε, γιατί το αρχείο δεν βρέθηκε στην προσωρινή αποθήκευση αρχείων", "Can’t increase permissions of %s" : "Αδυναμία αύξησης των δικαιωμάτων του %s", "Files can’t be shared with delete permissions" : "Δεν μπορεί να γίνει διαμοιρασμός αρχείων με δικαιώματα διαγραφής", "Files can’t be shared with create permissions" : "Δεν μπορεί να γίνει διαμοιρασμός αρχείων με δικαιώματα δημιουργίας", "Expiration date is in the past" : "Η ημερομηνία λήξης είναι στο παρελθόν", "Can’t set expiration date more than %s days in the future" : "Δεν είναι δυνατό να τεθεί η ημερομηνία λήξης σε περισσότερες από %s ημέρες στο μέλλον", "%s shared »%s« with you" : "Ο %s διαμοιράστηκε μαζί σας το »%s«", "%s via %s" : "%s μέσω %s", "The requested share does not exist anymore" : "Το διαμοιρασμένο που ζητήθηκε δεν υπάρχει πλέον", "Could not find category \"%s\"" : "Αδυναμία εύρεσης κατηγορίας \"%s\"", "Sunday" : "Κυριακή", "Monday" : "Δευτέρα", "Tuesday" : "Τρίτη", "Wednesday" : "Τετάρτη", "Thursday" : "Πέμπτη", "Friday" : "Παρασκευή", "Saturday" : "Σάββατο", "Sun." : "Κυρ.", "Mon." : "Δευ.", "Tue." : "Τρί.", "Wed." : "Τετ.", "Thu." : "Πέμ.", "Fri." : "Παρ.", "Sat." : "Σαβ.", "Su" : "Κυ", "Mo" : "Δε", "Tu" : "Τρ", "We" : "Τε", "Th" : "Πε", "Fr" : "Πα", "Sa" : "Σα", "January" : "Ιανουάριος", "February" : "Φεβρουάριος", "March" : "Μάρτιος", "April" : "Απρίλιος", "May" : "Μάϊος", "June" : "Ιούνιος", "July" : "Ιούλιος", "August" : "Αύγουστος", "September" : "Σεπτέμβριος", "October" : "Οκτώβριος", "November" : "Νοέμβριος", "December" : "Δεκέμβριος", "Jan." : "Ιαν.", "Feb." : "Φεβ.", "Mar." : "Μαρ.", "Apr." : "Απρ.", "May." : "Μαι.", "Jun." : "Ιουν.", "Jul." : "Ιουλ.", "Aug." : "Αυγ.", "Sep." : "Σεπ.", "Oct." : "Οκτ.", "Nov." : "Νοε.", "Dec." : "Δεκ.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Μόνο οι ακόλουθοι χαρακτήρες επιτρέπονται στο όνομα χρήστη; \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Πρέπει να δοθεί έγκυρο όνομα χρήστη", "Username contains whitespace at the beginning or at the end" : "Το όνομα χρήστη περιέχει κενό διάστημα στην αρχή ή στο τέλος", "Username must not consist of dots only" : "Το όνομα χρήστη δεν πρέπει να περιέχει μόνο τελείες", "A valid password must be provided" : "Πρέπει να δοθεί έγκυρο συνθηματικό", "The username is already being used" : "Το όνομα χρήστη είναι κατειλημμένο", "User disabled" : "Ο χρήστης απενεργοποιήθηκε", "Login canceled by app" : "Η είσοδος ακυρώθηκε από την εφαρμογή", "No app name specified" : "Δεν προδιορίστηκε όνομα εφαρμογής", "App '%s' could not be installed!" : "Δεν μπορεί να εγκατασταθεί η εφαρμογή '%s'!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Αυτή η εφαρμογή %s δεν μπορεί να εγκατασταθεί διότι δεν πληρούνται οι ακόλουθες εξαρτήσεις: %s", "a safe home for all your data" : "ένα ασφαλές μέρος για όλα τα δεδομένα σας", "File is currently busy, please try again later" : "Το αρχείο χρησιμοποιείται αυτή τη στιγμή, παρακαλώ προσπαθήστε αργότερα", "Can't read file" : "Αδυναμία ανάγνωσης αρχείου", "Application is not enabled" : "Δεν ενεργοποιήθηκε η εφαρμογή", "Authentication error" : "Σφάλμα πιστοποίησης", "Token expired. Please reload page." : "Το αναγνωριστικό έληξε. Παρακαλώ φορτώστε ξανά την σελίδα.", "Unknown user" : "Άγνωστος χρήστης", "No database drivers (sqlite, mysql, or postgresql) installed." : "Δεν βρέθηκαν εγκατεστημένοι οδηγοί βάσεων δεδομένων (sqlite, mysql, or postgresql).", "Cannot write into \"config\" directory" : "Αδυναμία εγγραφής στον κατάλογο \"config\"", "Cannot write into \"apps\" directory" : "Αδυναμία εγγραφής στον κατάλογο \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Αυτό συνήθως μπορεί να διορθωθεί δίνοντας δικαιώματα εγγραφής στον κατάλογο apps στον διακομιστή ιστού ή απενεργοποιώντας το appstore στο αρχείο διαμόρφωσης. Δείτε το %s", "Cannot create \"data\" directory" : "Αδυναμία δημιουργίας του καταλόγου \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Αυτό μπορεί συνήθως να διορθωθεί δίνοντας στον διακομιστή ιστού δικαιώματα εγγραφής στον βασικό κατάλογο. Δείτε το%s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Τα δικαιώματα πρόσβασης μπορούν συνήθως να διορθωθούν δίνοντας δικαιώματα εγγραφής στον βασικό κατάλογο στον διακομιστή ιστού. Δείτε το%s.", "Setting locale to %s failed" : "Ρύθμιση τοπικών ρυθμίσεων σε %s απέτυχε", "Please install one of these locales on your system and restart your webserver." : "Παρακαλώ να εγκαταστήσετε μία από αυτές τις τοπικές ρυθμίσεις στο σύστημά σας και να επανεκκινήσετε τον διακομιστή δικτύου σας.", "Please ask your server administrator to install the module." : "Παρακαλώ ζητήστε από το διαχειριστή του διακομιστή σας να εγκαταστήσει τη μονάδα.", "PHP module %s not installed." : "Η μονάδα %s PHP δεν είναι εγκατεστημένη. ", "PHP setting \"%s\" is not set to \"%s\"." : "Η ρύθμιση \"%s\"της PHP δεν είναι ορισμένη σε \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Προσαρμόζοντας αυτήν τη ρύθμιση στο php.ini το Nextcloud θα εκτελεστεί ξανά", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "Το mbstring.func_overload έχει ορισθεί σε \"%s\" αντί για την αναμενόμενη τιμή \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Για να διορθώσετε αυτό το πρόβλημα ορίστε το <code>mbstring.func_overload</code> σε <code>0</code> στο αρχείο php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Απαιτείται τουλάχιστον το libxml2 2.7.0. Αυτή τη στιγμή είναι εγκατεστημένο το %s.", "To fix this issue update your libxml2 version and restart your web server." : "Για να διορθώσετε το σφάλμα ενημερώστε την έκδοση του libxml2 και επανεκκινήστε τον διακομιστή.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Η PHP φαίνεται να είναι ρυθμισμένη ώστε να αφαιρεί inline doc blocks. Αυτό θα καταστήσει πολλές βασικές εφαρμογές μη διαθέσιμες.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Αυτό πιθανόν προκλήθηκε από προσωρινή μνήμη (cache)/επιταχυντή όπως τη Zend OPcache ή τον eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Κάποια αρθρώματα της PHP έχουν εγκατασταθεί, αλλά είναι ακόμα καταγεγραμμένες ως εκλιπόντα;", "Please ask your server administrator to restart the web server." : "Παρακαλώ ζητήστε από το διαχειριστή του διακομιστή σας να επανεκκινήσει το διακομιστή δικτύου σας.", "PostgreSQL >= 9 required" : "Απαιτείται PostgreSQL >= 9", "Please upgrade your database version" : "Παρακαλώ αναβαθμίστε την έκδοση της βάσης δεδομένων σας", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Παρακαλώ αλλάξτε τις ρυθμίσεις σε 0770 έτσι ώστε ο κατάλογος να μην μπορεί να προβάλλεται από άλλους χρήστες.", "Your data directory is readable by other users" : "Ο κατάλογος δεδομένων σας είναι διαθέσιμος προς ανάγνωση από άλλους χρήστες", "Your data directory must be an absolute path" : "Ο κατάλογος δεδομένων σας πρέπει να είναι απόλυτη διαδρομή", "Check the value of \"datadirectory\" in your configuration" : "Ελέγξτε την τιμή του \"Φάκελος Δεδομένων\" στις ρυθμίσεις σας", "Your data directory is invalid" : "Ο κατάλογος δεδομένων σας δεν είναι έγκυρος", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Εξασφαλίστε ότι υπάρχει ένα αρχείο με όνομα \".ocdata\" στον βασικό κατάλογο του καταλόγου δεδομένων.", "Could not obtain lock type %d on \"%s\"." : "Αδυναμία ανάκτησης τύπου κλειδιού %d στο \"%s\".", "Storage unauthorized. %s" : "Αποθηκευτικός χώρος χωρίς εξουσιοδότηση. %s", "Storage incomplete configuration. %s" : "Ελλιπής διαμόρφωση αποθηκευτικού χώρου. %s", "Storage connection error. %s" : "Σφάλμα σύνδεσης με αποθηκευτικό χώρο. %s", "Storage is temporarily not available" : "Μη διαθέσιμος χώρος αποθήκευσης προσωρινά", "Storage connection timeout. %s" : "Λήξη χρονικού ορίου σύνδεσης με αποθηκευτικό χώρο.%s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Αυτό μπορεί συνήθως να διορθωθεί %sπαρέχοντας δικαιώματα εγγραφής για το φάκελο config στο διακομιστή δικτύου%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Το άρθρωμα με id: %s δεν υπάρχει. Παρακαλώ ενεργοποιήστε το από τις ρυθμίσεις των εφαρμογών ή επικοινωνήστε με τον διαχειριστή.", "Server settings" : "Ρυθμίσεις διακομιστή", "DB Error: \"%s\"" : "Σφάλμα Βάσης Δεδομένων: \"%s\"", "Offending command was: \"%s\"" : "Η εντολη παραβατικοτητας ηταν: \"%s\"", "You need to enter either an existing account or the administrator." : "Χρειάζεται να εισάγετε είτε έναν υπάρχον λογαριασμό ή του διαχειριστή.", "Offending command was: \"%s\", name: %s, password: %s" : "Η εντολη παραβατικοτητας ηταν: \"%s\", ονομα: %s, κωδικος: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Ο ορισμός δικαιωμάτων για το %s απέτυχε, γιατί τα δικαιώματα υπερτερούν αυτά που είναι ορισμένα για το %s", "Setting permissions for %s failed, because the item was not found" : "Ο ορισμός δικαιωμάτων για το %s απέτυχε, γιατί το αντικείμενο δεν βρέθηκε", "Cannot clear expiration date. Shares are required to have an expiration date." : "Δεν είναι σαφής η ημερομηνία λήξης. Ο διαμοιρασμός πρέπει να έχει ημερομηνία λήξης", "Cannot increase permissions of %s" : "Αδυναμία αύξησης των δικαιωμάτων του %s", "Files can't be shared with delete permissions" : "Δεν μπορεί να γίνει διαμοιρασμός αρχείων με δικαιώματα διαγραφής", "Files can't be shared with create permissions" : "Δεν μπορεί να γίνει διαμοιρασμός αρχείων με δικαιώματα δημιουργίας", "Cannot set expiration date more than %s days in the future" : "Δεν είναι δυνατό να τεθεί η ημερομηνία λήξης σε περισσότερες από %s ημέρες στο μέλλον", "Personal" : "Προσωπικά", "Admin" : "Διαχείριση", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Αυτό μπορεί συνήθως να διορθωθεί %sδίνοντας διακαιώματα εγγραφής για τον κατάλογο εφαρμογών στο διακομιστή δικτύου%s ή απενεργοποιώντας το κέντρο εφαρμογών στο αρχείο config.", "Cannot create \"data\" directory (%s)" : "Αδυναμία δημιουργίας του καταλόγου \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Αυτό μπορεί συνήθως να διορθωθεί<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> δίνοντας στον διακομιστή ιστού δικαιώματα εγγραφής στον βασικό κατάλογο</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Τα δικαιώματα πρόσβασης μπορούν συνήθως να διορθωθούν %sδίνοντας δικαιώματα εγγραφής για τον βασικό κατάλογο στο διακομιστή δικτύου%s.", "Data directory (%s) is readable by other users" : "Ο κατάλογος δεδομένων (%s) είναι διαθέσιμος προς ανάγνωση από άλλους χρήστες", "Data directory (%s) must be an absolute path" : "Κατάλογος δεδομένων (%s) πρεπει να είναι απόλυτη η διαδρομή", "Data directory (%s) is invalid" : "Ο κατάλογος δεδομένων (%s) είναι άκυρος", "Please check that the data directory contains a file \".ocdata\" in its root." : "Παρακαλώ ελέγξτε ότι ο κατάλογος δεδομένων περιέχει ένα αρχείο \".ocdata\" στη βάση του." }, "nplurals=2; plural=(n != 1);"); l10n/he.js 0000604 00000046355 15247130447 0006270 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "לא ניתן לכתוב לתיקיית \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "בדרך כלל ניתן לפתור את הבעיה על ידי כך שנותנים ל- webserver הרשאות כניסה לתיקיית confg", "See %s" : "ניתן לראות %s", "Sample configuration detected" : "התגלתה דוגמת תצורה", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "התגלה שדוגמת התצורה הועתקה. דבר זה עלול לשבור את ההתקנה ולא נתמך.יש לקרוא את מסמכי התיעוד לפני שמבצעים שינויים ב- config.php", "PHP %s or higher is required." : "נדרש PHP בגרסת %s ומעלה.", "PHP with a version lower than %s is required." : "נדרש PHP בגרסה נמוכה מ- %s.", "%sbit or higher PHP required." : "נדרש PHP בגרסת %s ומעלה.", "Following databases are supported: %s" : "מסדי הנתונים הבאים נתמכים: %s", "The command line tool %s could not be found" : "כלי שורת הפקודה %s לא אותר", "The library %s is not available." : "הספריה %s אינה זמינה.", "Library %s with a version higher than %s is required - available version %s." : "ספריה %s בגרסה גבוהה מ- %s נדרשת - גרסה זמינה %s.", "Library %s with a version lower than %s is required - available version %s." : "ספריה %s בגרסה נמוכה מ- %s נדרשת - גרסה זמינה %s.", "Following platforms are supported: %s" : "הפלטפורמות הבאות נתמכות: %s", "Unknown filetype" : "סוג קובץ לא מוכר", "Invalid image" : "תמונה לא חוקית", "today" : "היום", "yesterday" : "אתמול", "_%n day ago_::_%n days ago_" : ["לפני %n יום","לפני %n ימים"], "last month" : "חודש שעבר", "last year" : "שנה שעברה", "_%n year ago_::_%n years ago_" : ["לפני %n שנה","לפני %n שנים"], "seconds ago" : "שניות", "File name is a reserved word" : "שם קובץ הנו מילה שמורה", "File name contains at least one invalid character" : "שם קובץ כולל לפחות תו אחד לא חוקי", "File name is too long" : "שם קובץ ארוך מדי", "Dot files are not allowed" : "קבצי Dot אינם מותרים", "Empty filename is not allowed" : "שם קובץ ריק אינו מאושר", "App \"%s\" cannot be installed because appinfo file cannot be read." : "יישום \"%s\" לא ניתן להתקנה כיוון שקובץ appinfo לא ניתן לקריאה.", "Help" : "עזרה", "Apps" : "יישומים", "Settings" : "הגדרות", "Log out" : "התנתק", "Users" : "משתמשים", "Sharing" : "שיתוף", "Tips & tricks" : "טיפים וטריקים", "%s enter the database username and name." : "%s יש להכניס את שם המשתמש ושם מסד הנתונים.", "%s enter the database username." : "%s נכנס למסד נתוני שמות המשתמשים.", "%s enter the database name." : "%s נכנס למסד נתוני השמות.", "%s you may not use dots in the database name" : "%s לא ניתן להשתמש בנקודות בשם מסד הנתונים", "Oracle connection could not be established" : "לא ניתן היה ליצור חיבור Oracle", "Oracle username and/or password not valid" : "שם משתמש ו/או סיסמת Oracle אינם תקפים", "PostgreSQL username and/or password not valid" : "שם משתמש ו/או סיסמת PostgreSQL אינם תקפים", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X אינו נתמך ו- %s לא יעבוד כשורה בפלטפורמה זו. ניתן לקחת סיכון ולהשתמש באחריותך! ", "For the best results, please consider using a GNU/Linux server instead." : "לתוצאות הכי טובות, יש לשקול שימוש בשרת GNU/Linux במקום.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "נראה ש- %s עובד על בסיס סביבת 32-bit PHP ושה- open_basedir הוגדר בקובץ php.ini. מצב זה יוביל לבעיות עם קבצים הגדולים מ- 4 GB ואינו מומלץ לחלוטין.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "יש להסיר את הגדרת open_basedir מתוך קובץ php.ini או להחליף לסביבת 64-bit PHP.", "Set an admin username." : "קביעת שם משתמש מנהל", "Set an admin password." : "קביעת סיסמת מנהל", "Can't create or write into the data directory %s" : "לא ניתן ליצור או לכתוב לתוך תיקיית הנתונים %s", "Invalid Federated Cloud ID" : "זיהוי ענן מאוגד לא חוקי", "Sharing %s failed, because the backend does not allow shares from type %i" : "השיתוף %s נכשל, כיוון שהצד האחורי אינו מאפשר שיתופים מסוג %i", "Sharing %s failed, because the file does not exist" : "השיתוף %s נכשל, כיוון שהקובץ אינו קיים", "You are not allowed to share %s" : "אינך רשאי/ת לשתף %s", "Sharing %s failed, because you can not share with yourself" : "השיתוף %s נכשל, כיוון שלא ניתן לשתף עם עצמך", "Sharing %s failed, because the user %s does not exist" : "השיתוף %s נכשל, כיוון שהמשתמש %s אינו קיים", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "השיתוף %s נכשל, כיוון שהמשתמש %s אינו חבר בקבוצות ש- %s חבר ב-", "Sharing %s failed, because this item is already shared with %s" : "שיתוף %s נכשל, כיוון שפריט זה כבר משותף עם %s", "Sharing %s failed, because this item is already shared with user %s" : "השיתוף %s נכשל, כיוון שהפריט כבר משותף עם משתמש %s", "Sharing %s failed, because the group %s does not exist" : "השיתוף %s נכשל, כיוון שהקבוצה %s אינה קיימת", "Sharing %s failed, because %s is not a member of the group %s" : "השיתוף %s נכשל, כיוון ש- %s אינו חבר בקבוצה %s", "You need to provide a password to create a public link, only protected links are allowed" : "יש לספק סיסמא ליצירת קישור ציבורי, רק קישורים מוגנים מותרים", "Sharing %s failed, because sharing with links is not allowed" : "השיתוף %s נכשל, כיוון ששיתוף עם קישור אינו מותר", "Not allowed to create a federated share with the same user" : "אסור ליצור שיתוף מאוגד עם אותו משתמש", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "שיתוף %s נכשל, לא ניתן לאתר %s, ייתכן שהשרת לא ניתן להשגה כרגע.", "Share type %s is not valid for %s" : "שיתוף מסוג %s אינו תקף ל- %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "לא ניתן לקבוע תאריך תפוגה. שיתופים אינם יכולים לפוג תוקף מאוחר יותר מ- %s לאחר ששותפו", "Cannot set expiration date. Expiration date is in the past" : "לא ניתן לקבוע תאריך תפוגה. תאריך התפוגה הנו בעבר", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "צד אחורי לשיתוף %s חייב ליישם את ממשק OCP\\Share_Backend", "Sharing backend %s not found" : "צד אחורי לשיתוף %s לא נמצא", "Sharing backend for %s not found" : "צד אחורי לשיתוף של %s לא נמצא", "Sharing failed, because the user %s is the original sharer" : "שיתוף נכשל, כיוון שמשתמש %s הנו המשתף המקורי", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "השיתוף %s נכשל, כיוון שההרשאות עלו על ההרשאות שניתנו ל- %s", "Sharing %s failed, because resharing is not allowed" : "השיתוף %s נכשל, כיוון ששיתוף מחודש אסור", "Sharing %s failed, because the sharing backend for %s could not find its source" : "השיתוף %s נכשל, כיוון שבצד אחורי לשיתוף עבור %s לא ניתן היה לאתר את מקורו", "Sharing %s failed, because the file could not be found in the file cache" : "השיתוף %s נכשל, כייון שלא ניתן היה למצוא את הקובץ בזכרון המטמון", "Expiration date is in the past" : "תאריך תפוגה הנו בעבר", "%s shared »%s« with you" : "%s שיתף/שיתפה איתך את »%s«", "%s via %s" : "%s על בסיס %s", "Could not find category \"%s\"" : "לא ניתן למצוא את הקטגוריה „%s“", "Sunday" : "יום ראשון", "Monday" : "יום שני", "Tuesday" : "יום שלישי", "Wednesday" : "יום רביעי", "Thursday" : "יום חמישי", "Friday" : "יום שישי", "Saturday" : "שבת", "Sun." : "ראשון", "Mon." : "שני", "Tue." : "שלישי", "Wed." : "רביעי", "Thu." : "חמישי", "Fri." : "שישי", "Sat." : "שבת", "Su" : "א", "Mo" : "ב", "Tu" : "ג", "We" : "ד", "Th" : "ה", "Fr" : "ו", "Sa" : "ש", "January" : "ינואר", "February" : "פברואר", "March" : "מרץ", "April" : "אפריל", "May" : "מאי", "June" : "יוני", "July" : "יולי", "August" : "אוגוסט", "September" : "ספטמבר", "October" : "אוקטובר", "November" : "נובמבר", "December" : "דצמבר", "Jan." : "ינו׳", "Feb." : "פבר׳", "Mar." : "מרץ", "Apr." : "אפר׳", "May." : "מאי", "Jun." : "יונ׳", "Jul." : "יול׳", "Aug." : "אוג׳", "Sep." : "ספט׳", "Oct." : "אוק׳", "Nov." : "נוב׳", "Dec." : "דצמ׳", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "רק התווים הבאים מאושרים לשם משתמש: \"a-z\", \"A-Z\", \"0-9\", וגם \"_.@-'\"", "A valid username must be provided" : "יש לספק שם משתמש תקני", "Username contains whitespace at the beginning or at the end" : "שם המשתמש מכיל רווח בתחילתו או בסופו", "A valid password must be provided" : "יש לספק ססמה תקנית", "The username is already being used" : "השם משתמש כבר בשימוש", "User disabled" : "משתמש מנוטרל", "Login canceled by app" : "התחברות בוטלה על ידי יישום", "No app name specified" : "לא הוגדר שם יישום", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "היישום \"%s\" לא ניתן להתקנה כיוון שיחסי התלות הבאים אינם מתקיימים: %s", "a safe home for all your data" : "בית בטוח עבור כל המידע שלך", "File is currently busy, please try again later" : "הקובץ בשימוש כרגע, יש לנסות שוב מאוחר יותר", "Can't read file" : "לא ניתן לקרוא קובץ", "Application is not enabled" : "יישומים אינם מופעלים", "Authentication error" : "שגיאת הזדהות", "Token expired. Please reload page." : "פג תוקף. נא לטעון שוב את הדף.", "Unknown user" : "משתמש לא ידוע", "No database drivers (sqlite, mysql, or postgresql) installed." : "לא מותקנים דרייברים למסד הנתונים (sqlite, mysql, או postgresql).", "Cannot write into \"config\" directory" : "לא ניתן לכתוב לתיקיית \"config\"!", "Cannot write into \"apps\" directory" : "לא ניתן לכתוב לתיקיית \"apps\"", "Setting locale to %s failed" : "הגדרת שפה ל- %s נכשלה", "Please install one of these locales on your system and restart your webserver." : "יש להתקין אחת מהשפות על המערכת שלך ולהפעיל מחדש את שרת האינטרנט.", "Please ask your server administrator to install the module." : "יש לבקש ממנהל השרת שלך להתקין את המודול.", "PHP module %s not installed." : "מודול PHP %s אינו מותקן.", "PHP setting \"%s\" is not set to \"%s\"." : "הגדרות PHP \"%s\" אינם מוגדרות ל- \"%s\"", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload מוגדר ל- \"%s\" במקום הערך המצופה \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "לתיקון בעיה זו יש להגדיר <code>mbstring.func_overload</code> כ- <code>0</code> iבקובץ ה- php.ini שלך", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 נדרש לכל הפחות. כרגע %s מותקן.", "To fix this issue update your libxml2 version and restart your web server." : "לתיקון הבעיה יש לעדכן את גרסת ה- libxml2 שלך ולהפעיל מחדש את שרת האינטרנט שלך.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ככל הנראה מוגדר ל- strip inline doc blocks. זה יגרום למספר יישומי ליבה לא להיות נגישים.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "זה ככל הנראה נגרם על ידי מאיץ/מטמון כמו Zend OPcache או eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "מודולי PHP הותקנו, אך עדיין רשומים כחסרים?", "Please ask your server administrator to restart the web server." : "יש לבקש ממנהל השרת שלך להפעיל מחדש את שרת האינטרנט.", "PostgreSQL >= 9 required" : "נדרש PostgreSQL >= 9", "Please upgrade your database version" : "יש לשדרג את גרסת מסד הנתונים שלך", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "יש לשנות את ההרשאות ל- 0770 כך שהתיקייה לא תרשם על ידי משתמשים אחרים.", "Check the value of \"datadirectory\" in your configuration" : "יש לבדוק את הערך \"datadirectory\" בהגדרות התצורה שלך", "Could not obtain lock type %d on \"%s\"." : "לא ניתן היה להשיג סוג נעילה %d ב- \"%s\".", "Storage unauthorized. %s" : "אחסון לא מורשה. %s", "Storage incomplete configuration. %s" : "תצורה לא מושלמת של האחסון. %s", "Storage connection error. %s" : "שגיאת חיבור אחסון. %s", "Storage connection timeout. %s" : "פסק זמן חיבור אחסון. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "בדרך כלל ניתן לפתור את הבעיה על ידי כך ש- %s נותן ל- webserver הרשאות כניסה לתיקיית config %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "מודול עם זהות: %s אינו קיים. יש לאפשר את זה בהגדרות היישומים או ליצור קשר עם המנהל.", "Server settings" : "הגדרות שרת", "DB Error: \"%s\"" : "שגיאת מסד נתונים: \"%s\"", "Offending command was: \"%s\"" : "הפקודה המזיקה הייתה: \"%s\"", "You need to enter either an existing account or the administrator." : "יש להכניס חשבון קיים או מנהל.", "Offending command was: \"%s\", name: %s, password: %s" : "הפקודה המזיקה הייתה: \"%s\", שם: %s, סיסמא: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "הגדרת הרשאות ל- %s נכשלה, כיוון שההרשאות עולים על האישורים שניתנו ל- %s", "Setting permissions for %s failed, because the item was not found" : "הגדרת הרשאות ל- %s נכשלה, כיוון שהפריט לא נמצא", "Cannot clear expiration date. Shares are required to have an expiration date." : "לא ניתן לבטל תאריך תפוגה. שיתופים חייבים להכיל תאריך תפוגה.", "Cannot increase permissions of %s" : "לא ניתן להגדיל את ההיתרים של %s", "Files can't be shared with delete permissions" : "קובץ לא ניתן לשיתוף בפעולת מחיקת הרשאות", "Files can't be shared with create permissions" : "קובץ לא ניתן לשיתוף בפעולת יצירת הרשאות", "Cannot set expiration date more than %s days in the future" : "לא ניתן להגדיר את תאריך התפוגה מעל %s ימים בעתיד", "Personal" : "אישי", "Admin" : "מנהל", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "בדרך כלל ניתן להסתדר על ידי %s מתן הרשאות כתיבה בשרת האינטרנט לתיקיית היישומים %s או נטרול חנות היישומים בקובץ ה- config.", "Cannot create \"data\" directory (%s)" : "לא ניתן ליצור תיקיית \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "זה בדרך כלל ניתן לתיקון על ידי <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">מתן הרשאות כתיבה בשרת לתיקיית הבסיס directory</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "הרשאות ניתנות בדרך כלל לתיקון על ידי %s מתן לשרת האינטרנט גישת כתיבה לתיקיית הבסיס %s.", "Data directory (%s) is readable by other users" : "תיקיית המידע (%s) ניתנת לקריאה על ידי משתמשים אחרים", "Data directory (%s) must be an absolute path" : "תיקיית המידע (%s) חייבת להיות כנתיב אבסולוטי", "Data directory (%s) is invalid" : "תיקיית מידע (%s) אינה חוקית", "Please check that the data directory contains a file \".ocdata\" in its root." : "יש לוודא שתיקיית המידע כוללת קובץ \".ocdata\" בנתיב הבסיס שלה" }, "nplurals=2; plural=(n != 1);"); l10n/ja.json 0000604 00000062114 15247130447 0006612 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "\"config\"ディレクトリに書き込めません!", "This can usually be fixed by giving the webserver write access to the config directory" : "多くの場合、これはWebサーバーにconfigディレクトリへの書き込み権限を与えることで解決できます。", "See %s" : "%s を閲覧", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "多くの場合、Webサーバーの configディレクトリ に書き込み権限を与えることで直ります。%s を見てください", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "アプリ %1$s のファイルが正しく置き換えられませんでした。サーバーと互換性のあるバージョンであることを確認してください。", "Sample configuration detected" : "サンプル設定が見つかりました。", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "サンプル設定がコピーされてそのままです。このままではインストールが失敗し、サポート対象外になります。config.phpを変更する前にドキュメントを確認してください。", "%1$s and %2$s" : "%1$s と %2$s", "%1$s, %2$s and %3$s" : "%1$s と %2$s、%3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s と %2$s、%3$s、%4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s と %2$s、%3$s、%4$s、%5$s", "Education Edition" : "Education Edition", "PHP %s or higher is required." : "PHP %s 以上が必要です。", "PHP with a version lower than %s is required." : "%s 以前のバージョンのPHPが必要です。", "%sbit or higher PHP required." : "%sbit 以上の新しいバージョンのPHPが必要です。", "Following databases are supported: %s" : "次のデータベースをサポートしています: %s", "The command line tool %s could not be found" : "コマンド '%s' は見つかりませんでした。", "The library %s is not available." : " %s ライブラリーが利用できません。", "Library %s with a version higher than %s is required - available version %s." : "%s ライブラリーは、%s よりも新しいバージョンが必要です。利用可能なバージョンは、 %s です。", "Library %s with a version lower than %s is required - available version %s." : "%s ライブラリーは、%s よりも古いバージョンが必要です。利用可能なバージョンは、 %s です。", "Following platforms are supported: %s" : "次のプラットフォームをサポートしています: %s", "Server version %s or higher is required." : "サーバーの %s よりも高いバージョンが必要です。", "Server version %s or lower is required." : "サーバーの %s よりも低いバージョンが必要です。", "Unknown filetype" : "不明なファイルタイプ", "Invalid image" : "無効な画像", "Avatar image is not square" : "アバター画像が正方形ではありません", "today" : "今日", "yesterday" : "1日前", "_%n day ago_::_%n days ago_" : ["%n 日前"], "last month" : "1ヶ月前", "_%n month ago_::_%n months ago_" : ["%nヶ月前"], "last year" : "1年前", "_%n year ago_::_%n years ago_" : ["%n 年前"], "_%n hour ago_::_%n hours ago_" : ["%n 時間前"], "_%n minute ago_::_%n minutes ago_" : ["%n 分前"], "seconds ago" : "数秒前", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID: %sのモジュールは存在しません。アプリ設定で有効にするか、管理者に問い合わせてください。", "File name is a reserved word" : "ファイル名が予約された単語です", "File name contains at least one invalid character" : "ファイル名に1文字以上の無効な文字が含まれています", "File name is too long" : "ファイル名が長すぎます", "Dot files are not allowed" : "ドットファイルは許可されていません", "Empty filename is not allowed" : "空のファイル名は許可されていません", "App \"%s\" cannot be installed because appinfo file cannot be read." : "appinfoファイルが読み込めないため、アプリ名 \"%s\" がインストールできません。", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "\"%s\" アプリは、このバージョンのサーバーと互換性がないためインストールされませんでした。", "This is an automatically sent email, please do not reply." : "これは自動的に生成されたメールです。返信しないでください。", "Help" : "ヘルプ", "Apps" : "アプリ", "Settings" : "設定", "Log out" : "ログアウト", "Users" : "ユーザー", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "基本設定", "Sharing" : "共有", "Security" : "セキュリティ", "Encryption" : "暗号化", "Additional settings" : "追加設定", "Tips & tricks" : "ヒントとコツ", "Personal info" : "個人情報", "Sync clients" : "同期クライアント", "Unlimited" : "無制限", "__language_name__" : "Japanese (日本語)", "Verifying" : "検証中", "Verifying …" : "検証中", "Verify" : "検証", "%s enter the database username and name." : "%s データベース名とデータベースのユーザー名を入力してください。", "%s enter the database username." : "%s のデータベースのユーザー名を入力してください。", "%s enter the database name." : "%s のデータベース名を入力してください。", "%s you may not use dots in the database name" : "%s ではデータベース名にドットを利用できないかもしれません。", "Oracle connection could not be established" : "Oracleへの接続が確立できませんでした。", "Oracle username and/or password not valid" : "Oracleのユーザー名もしくはパスワードは有効ではありません", "PostgreSQL username and/or password not valid" : "PostgreSQLのユーザー名もしくはパスワードは有効ではありません", "You need to enter details of an existing account." : "既存のアカウントの詳細を入力してください。", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X では、サポートされていません。このOSでは、%sは正常に動作しないかもしれません。ご自身の責任においてご利用ください。", "For the best results, please consider using a GNU/Linux server instead." : "最も良い方法としては、代わりにGNU/Linuxサーバーを利用することをご検討ください。", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "このインスタンス %s は、32bit PHP 環境で動作しており、php.ini に open_basedir が設定されているようです。4GB以上のファイルで問題が発生するため、この設定を利用しないことをお勧めします。", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "php.ini から open_basedir 設定を削除するか、64bit PHPに切り替えてください。", "Set an admin username." : "管理者のユーザー名を設定", "Set an admin password." : "管理者のパスワードを設定", "Can't create or write into the data directory %s" : "%s データディレクトリに作成、書き込みができません", "Invalid Federated Cloud ID" : "無効な統合されたクラウドID", "Sharing %s failed, because the backend does not allow shares from type %i" : "%s を共有できませんでした。%i タイプからの共有は許可されていません。", "Sharing %s failed, because the file does not exist" : "%s を共有できませんでした。そのファイルは存在しません。", "You are not allowed to share %s" : "%s を共有することを許可されていません。", "Sharing %s failed, because you can not share with yourself" : "%s を共有できませんでした。自分自身に共有することはできません。", "Sharing %s failed, because the user %s does not exist" : "%s を共有できませんでした。ユーザー %s が存在しません。", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s を共有できませんでした。ユーザー %s はどのグループにも属していません。%s は、??のメンバーです。", "Sharing %s failed, because this item is already shared with %s" : "%s を共有できませんでした。このアイテムはすでに %s に共有されています。", "Sharing %s failed, because this item is already shared with user %s" : "%s を共有できませんでした。このアイテムは、ユーザー %s によりすでに共有されています。", "Sharing %s failed, because the group %s does not exist" : "%s を共有できませんでした。グループ %s は存在しません。", "Sharing %s failed, because %s is not a member of the group %s" : "%s を共有できませんでした。%s は、グループ %s のメンバーではありません。", "You need to provide a password to create a public link, only protected links are allowed" : "公開用リンクの作成にはパスワードの設定が必要です", "Sharing %s failed, because sharing with links is not allowed" : "%s を共有できませんでした。リンクでの共有は許可されていません。", "Not allowed to create a federated share with the same user" : "同じユーザーでフェデレーション共有を作成することは出来ません", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s を共有できませんでした。%s が見つかりませんでした。現在サーバーに接続できないようです。", "Share type %s is not valid for %s" : "%s の共有方法は、%s には適用できません。", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "有効期限を設定できません。共有開始から %s 以降に有効期限を設定することはできません。", "Cannot set expiration date. Expiration date is in the past" : "有効期限を設定できません。有効期限が過去を示しています。", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "%s のバックエンドの共有には、OCP\\Share_Backend インターフェースを実装しなければなりません。", "Sharing backend %s not found" : "共有バックエンド %s が見つかりません", "Sharing backend for %s not found" : "%s のための共有バックエンドが見つかりません", "Sharing failed, because the user %s is the original sharer" : "共有できませんでした。ユーザー %sは元々の共有者です。", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "%s を共有できませんでした。%s に許可されている権限を越えています。", "Sharing %s failed, because resharing is not allowed" : "%s を共有できませんでした。再共有は許可されていません。", "Sharing %s failed, because the sharing backend for %s could not find its source" : "%s の共有に失敗しました。%s のバックエンド共有に必要なソースが見つかりませんでした。", "Sharing %s failed, because the file could not be found in the file cache" : "%s の共有に失敗しました。ファイルキャッシュにファイルがありませんでした。", "Can’t increase permissions of %s" : "%s の権限を追加できません", "Files can’t be shared with delete permissions" : "削除権限つきでファイルを共有できません。", "Files can’t be shared with create permissions" : "作成権限つきでファイルを共有できません。", "Expiration date is in the past" : "有効期限が切れています", "Can’t set expiration date more than %s days in the future" : "有効期限を%s日以降に設定できません。", "%s shared »%s« with you" : "%sが あなたと »%s«を共有しました", "%s shared »%s« with you." : "%sが あなたと »%s«を共有しました", "Click the button below to open it." : "開くには下のボタンをクリック", "Open »%s«" : "»%s«を開く", "%s via %s" : "%s に %s から", "The requested share does not exist anymore" : "この共有はもう存在しません。", "Could not find category \"%s\"" : "カテゴリ \"%s\" が見つかりませんでした", "Sunday" : "日曜日", "Monday" : "月曜日", "Tuesday" : "火曜日", "Wednesday" : "水曜日", "Thursday" : "木曜日", "Friday" : "金曜日", "Saturday" : "土曜日", "Sun." : "日", "Mon." : "月", "Tue." : "火", "Wed." : "水", "Thu." : "木", "Fri." : "金", "Sat." : "土", "Su" : "日", "Mo" : "月", "Tu" : "火", "We" : "水", "Th" : "木", "Fr" : "金", "Sa" : "土", "January" : "1月", "February" : "2月", "March" : "3月", "April" : "4月", "May" : "5月", "June" : "6月", "July" : "7月", "August" : "8月", "September" : "9月", "October" : "10月", "November" : "11月", "December" : "12月", "Jan." : "1月", "Feb." : "2月", "Mar." : "3月", "Apr." : "4月", "May." : "5月", "Jun." : "6月", "Jul." : "7月", "Aug." : "8月", "Sep." : "9月", "Oct." : "10月", "Nov." : "11月", "Dec." : "12月", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "ユーザー名で利用できる文字列は、次のものです: \"a-z\", \"A-Z\", \"0-9\", \"_.@-\"", "A valid username must be provided" : "有効なユーザー名を指定する必要があります", "Username contains whitespace at the beginning or at the end" : "ユーザー名の最初か最後に空白が含まれています", "Username must not consist of dots only" : "ユーザー名は、ドットのみではつけられません", "A valid password must be provided" : "有効なパスワードを指定する必要があります", "The username is already being used" : "ユーザー名はすでに使われています", "User disabled" : "ユーザーは無効です", "Login canceled by app" : "アプリによりログインが中止されました", "No app name specified" : "アプリ名が未指定", "App '%s' could not be installed!" : "'%s' アプリをインストールできませんでした。", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "次の依存関係が満たされないため、\"%s\" アプリをインストールできません:%s", "a safe home for all your data" : "あなたの全データの安全な家", "File is currently busy, please try again later" : "現在ファイルはビジーです。後でもう一度試してください。", "Can't read file" : "ファイルを読み込めません", "Application is not enabled" : "アプリケーションは無効です", "Authentication error" : "認証エラー", "Token expired. Please reload page." : "トークンが無効になりました。ページを再読込してください。", "Unknown user" : "不明なユーザー", "No database drivers (sqlite, mysql, or postgresql) installed." : "データベースドライバー (sqlite, mysql, postgresql) がインストールされていません。", "Cannot write into \"config\" directory" : "\"config\" ディレクトリに書き込みができません", "Cannot write into \"apps\" directory" : "\"apps\" ディレクトリに書き込みができません", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "多くの場合、これは Webサーバーにappsディレクトリへの書き込み権限を与えるか、設定ファイルでアプリストアを無効化することで直ります。%s を見てください。", "Cannot create \"data\" directory" : "\"data\" ディレクトリを作成できません", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "多くの場合、Webサーバーのルートディレクトリに書き込み権限を与えることで直ります。%s を見てください。", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Webサーバーのルートディレクトリに書き込み権限パーミッションが必要です。%s を見てください。", "Setting locale to %s failed" : "ロケールを %s に設定できませんでした", "Please install one of these locales on your system and restart your webserver." : "これらのロケールのうちいずれかをシステムにインストールし、Webサーバーを再起動してください。", "Please ask your server administrator to install the module." : "サーバー管理者にモジュールのインストールを依頼してください。", "PHP module %s not installed." : "PHP のモジュール %s がインストールされていません。", "PHP setting \"%s\" is not set to \"%s\"." : "PHP設定の\"%s\"は \"%s\"に設定されていません", "Adjusting this setting in php.ini will make Nextcloud run again" : "php.ini のこの設定を調整して、再度 Nextcloudを起動してください。", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload の値は \"0\" であるべきですが、\"%s\" に設定されています", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "この問題を修正するには、php.ini ファイルの <code>mbstring.func_overload</code> を <code>0</code> に設定してください。", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 バージョン 2.7.0 が最低必要です。現在 %s がインストールされています。", "To fix this issue update your libxml2 version and restart your web server." : "この問題を解決するには、libxml2 を更新して、ウェブサーバーを再起動してください。", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHPでインラインドキュメントブロックを取り除く設定になっています。これによりコアアプリで利用できないものがいくつかあります。", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "これは、Zend OPcacheやeAccelerator 等のキャッシュ/アクセラレーターが原因かもしれません。", "PHP modules have been installed, but they are still listed as missing?" : "PHP モジュールはインストールされていますが、まだ一覧に表示されていますか?", "Please ask your server administrator to restart the web server." : "サーバー管理者にWebサーバーを再起動するよう依頼してください。", "PostgreSQL >= 9 required" : "PostgreSQL 9以上が必要です", "Please upgrade your database version" : "新しいバージョンのデータベースにアップグレードしてください", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "ディレクトリが他のユーザーから見えないように、パーミッションを 0770 に変更してください。", "Your data directory is readable by other users" : "データディレクトリは、他のユーザーから読み取り専用です", "Your data directory must be an absolute path" : "データディレクトリは、絶対パスにする必要があります", "Check the value of \"datadirectory\" in your configuration" : "設定ファイル内の \"datadirectory\" の値を確認してください。", "Your data directory is invalid" : "データディレクトリが無効です", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "データディレクトリの直下に \".ocdata\" ファイルがあるのを確認してください。", "Could not obtain lock type %d on \"%s\"." : "\"%s\" で %d タイプのロックを取得できませんでした。", "Storage unauthorized. %s" : "権限のないストレージです。 %s", "Storage incomplete configuration. %s" : "設定が未完了のストレージです。 %s", "Storage connection error. %s" : "ストレージへの接続エラー。 %s", "Storage is temporarily not available" : "ストレージは一時的に利用できません", "Storage connection timeout. %s" : "ストレージへの接続がタイムアウト。 %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "多くの場合、これは %s Webサーバーにconfigディレクトリ %s への書き込み権限を与えることで解決できます。", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "id: %sのモジュールは存在しません。アプリ設定で有効にするか、管理者に問い合わせてください。", "Server settings" : "サーバー設定", "DB Error: \"%s\"" : "DBエラー: \"%s\"", "Offending command was: \"%s\"" : "違反コマンド: \"%s\"", "You need to enter either an existing account or the administrator." : "既存のアカウントもしくは管理者のどちらかを入力する必要があります。", "Offending command was: \"%s\", name: %s, password: %s" : "違反コマンド: \"%s\"、名前: %s、パスワード: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "%s を共有できませんでした。%s に許可されている権限を越えています。", "Setting permissions for %s failed, because the item was not found" : "%s を共有できませんでした。アイテムが存在しません。", "Cannot clear expiration date. Shares are required to have an expiration date." : "有効期限を解除できません。共有するには有効期限を設定する必要があります。", "Cannot increase permissions of %s" : "%s の権限を強化できません", "Files can't be shared with delete permissions" : "削除権限つきでファイルを共有できません。", "Files can't be shared with create permissions" : "作成権限つきでファイルを共有できません。", "Cannot set expiration date more than %s days in the future" : "有効期限を%s日以降に設定できません。", "Personal" : "個人", "Admin" : "管理", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "多くの場合、これは %s Webサーバーにappsディレクトリ %s への書き込み権限を与えるか、設定ファイルでアプリストアを無効化することで解決できます。", "Cannot create \"data\" directory (%s)" : "\"data\" ディレクトリ (%s) を作成できません", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "通常、<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">Webサーバーにルートディレクトリへの書き込み権限を与える</a>ことで解決できます。", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "多くの場合、パーミッションは %s Webサーバーにルートディレクトリ %s への書き込み権限を与えることで解決できます。", "Data directory (%s) is readable by other users" : "データディレクトリ (%s) は他のユーザーも閲覧することができます", "Data directory (%s) must be an absolute path" : "データディレクトリ (%s) は、絶対パスである必要があります。", "Data directory (%s) is invalid" : "データディレクトリ (%s) は無効です", "Please check that the data directory contains a file \".ocdata\" in its root." : "データディレクトリに \".ocdata\" ファイルが含まれていることを確認してください。" },"pluralForm" :"nplurals=1; plural=0;" } l10n/sr.js 0000604 00000072415 15247130447 0006314 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Не могу да уписујем у „config“ директоријум!", "This can usually be fixed by giving the webserver write access to the config directory" : "Ово се обично може средити давањем права веб серверу да пише у директоријум са подешавањима", "See %s" : "Погледајте %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ово се обично може средити давањем права писања веб серверу за директоријум са подешавањима. Погледајте %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Фајлови апликације „%$1s“ нису правилно замењени. Проверите да ли је верзија компатибилна са сервером.", "Sample configuration detected" : "Откривен је пример подешавања", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Откривено је да је прекопиран пример подешавања. Ово може покварити инсталацију и није подржано. Прочитајте документацију пре вршења промена у фајлу config.php", "%1$s and %2$s" : "%1$s и %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s и %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s и %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s и %5$s", "Education Edition" : "Образовно издање", "Enterprise bundle" : "Комплет за предузећа", "Groupware bundle" : "Комплет за радне тимове", "Social sharing bundle" : "Комплет за друштвене мреже", "PHP %s or higher is required." : "Потребан је PHP %s или новији.", "PHP with a version lower than %s is required." : "Потребна је PHP верзија старија од верзије %s.", "%sbit or higher PHP required." : "Потребна је верзија PHP-а једнака или већа од верзије %s.", "Following databases are supported: %s" : "Подржане су следеће базе података: %s", "The command line tool %s could not be found" : "Алатку командне линије „%s“ није могуће пронаћи", "The library %s is not available." : "Библиотека „%s“ није доступна.", "Library %s with a version higher than %s is required - available version %s." : "Потребна је библиотека „%s“ верзије веће од %s - доступна верзија је %s.", "Library %s with a version lower than %s is required - available version %s." : "Потребна је библиотека „%s“ верзије ниже од %s - доступна верзија је %s.", "Following platforms are supported: %s" : "Подржане су следеће платформе: %s", "Server version %s or higher is required." : "Потребна је верзија сервера %s или виша.", "Server version %s or lower is required." : "Потребна је верзија сервера %s или нижа.", "Unknown filetype" : "Непознат тип фајла", "Invalid image" : "Неисправна слика", "Avatar image is not square" : "Слика аватара није квадратна", "today" : "данас", "yesterday" : "јуче", "_%n day ago_::_%n days ago_" : ["пре %n дан","пре %n дана","пре %n дана"], "last month" : "прошлог месеца", "_%n month ago_::_%n months ago_" : ["пре %n месец","пре %n месеца","пре %n месеци"], "last year" : "прошле године", "_%n year ago_::_%n years ago_" : ["пре %n годину","пре %n године","пре %n година"], "_%n hour ago_::_%n hours ago_" : ["пре %n сат","пре %n сата","пре %n сати"], "_%n minute ago_::_%n minutes ago_" : ["пре %n минут","пре %n минута","пре %n минута"], "seconds ago" : "пре неколико секунди", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модул са идентификацијом: %s не постоји. Омогућите га у подешавањима апликација или контактирајте администратора.", "File name is a reserved word" : "Назив фајла је резервисана реч", "File name contains at least one invalid character" : "Назив фајла садржи бар један недозвољен знак", "File name is too long" : "Назив фајла је предугачак", "Dot files are not allowed" : "Фајлови са почетном тачком нису дозвољени", "Empty filename is not allowed" : "Празан назив није дозвољен", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Апликација \"%s\" не може бити инсталирана јер appinfo фајл не може да се прочита.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Апликација \"%s\" не може бити инсталирана јер није компатибилна са овом верзијом сервера.", "This is an automatically sent email, please do not reply." : "Ово је аутоматски генерисана порука, не одговарајте на њу.", "Help" : "Помоћ", "Apps" : "Апликације", "Settings" : "Поставке", "Log out" : "Одјава", "Users" : "Корисници", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Основне поставке", "Sharing" : "Дељење", "Security" : "Безбедност", "Encryption" : "Шифровање", "Additional settings" : "Додатне поставке", "Tips & tricks" : "Савети и трикови", "Personal info" : "Лични подаци", "Sync clients" : "Клијенти у синхронизацији", "Unlimited" : "Неограничено", "__language_name__" : "Српски", "Verifying" : "Проверавам", "Verifying …" : "Проверавам ...", "Verify" : "Провери", "%s enter the database username and name." : "%s унеси корисничко име базе података и име.", "%s enter the database username." : "%s унеси корисничко име базе података.", "%s enter the database name." : "%s унеси име базе података.", "%s you may not use dots in the database name" : "%s не можете користити тачке у имену базе података", "Oracle connection could not be established" : "Веза са базом података Oracle не може бити успостављена", "Oracle username and/or password not valid" : "Oracle корисничко име и/или лозинка нису исправни", "PostgreSQL username and/or password not valid" : "PostgreSQL корисничко име и/или лозинка нису исправни", "You need to enter details of an existing account." : "Потребно је да унесете детаље постојећег налога.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Мек ОС Икс није подржан и %s неће радити исправно на овој платформи. Користите га на сопствени ризик!", "For the best results, please consider using a GNU/Linux server instead." : "За најбоље резултате, размотрите употребу ГНУ/Линукс сервера.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Изгледа да %s ради у 32-битном PHP окружењу а open_basedir је подешен у php.ini фајлу. То може довести до проблема са фајловима већим од 4 GB, те стога није препоручљиво.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Уклоните open_basedir поставку из php.ini фајла или пређите на 64-битни PHP.", "Set an admin username." : "Поставите име за администратора.", "Set an admin password." : "Поставите лозинку за администратора.", "Can't create or write into the data directory %s" : "Не могу креирати или уписивати у директоријум података %s", "Invalid Federated Cloud ID" : "Неисправан ИД Здруженог облака", "Sharing %s failed, because the backend does not allow shares from type %i" : "Дељење %s није успело зато што позадина не дозвољава дељење од типа %i", "Sharing %s failed, because the file does not exist" : "Дељење %s није успело зато што фајл не постоји", "You are not allowed to share %s" : "Није вам дозвољено да делите %s", "Sharing %s failed, because you can not share with yourself" : "Дељење %s није успело зато што не можете да делите са самим собом", "Sharing %s failed, because the user %s does not exist" : "Дељење %s није успело зато што не постоји корисник %s", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Дељење %s није успело зато што корисник %s није члан ниједне групе чији је %s члан", "Sharing %s failed, because this item is already shared with %s" : "Дељење %s није успело зато што се ова ставка већ дели са %s", "Sharing %s failed, because this item is already shared with user %s" : "Дељење %s није успело зато што се ова ставка већ дели са корисником %s", "Sharing %s failed, because the group %s does not exist" : "Дељење %s није успело зато што не постоји група %s", "Sharing %s failed, because %s is not a member of the group %s" : "Дељење %s није успело зато што %s није члан групе %s", "You need to provide a password to create a public link, only protected links are allowed" : "Морате да обезбедите лозинку за креирање јавне везе, дозвољене су само заштићене везе", "Sharing %s failed, because sharing with links is not allowed" : "Дељење %s није успело зато што дељење са везама није дозвољено", "Not allowed to create a federated share with the same user" : "Није дозвољено да направите здружено дељење са истим корисником", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Дељење %s није успело, није могуће пронаћи %s, можда сервер тренутно није доступан.", "Share type %s is not valid for %s" : "Тип фајла за дељење %s није исправан за %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Не могу поставити датум трајања. Дељења не могу истицати касније од %s пошто су активирана", "Cannot set expiration date. Expiration date is in the past" : "Не могу поставити датум трајања. Датум трајања употребе је у прошлости", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Позадина дељења %s мора користити корисничко окружење OCP\\Share_Backend", "Sharing backend %s not found" : "Позадина за дељење %s није пронађена", "Sharing backend for %s not found" : "Позадина за дељење за %s није пронађена", "Sharing failed, because the user %s is the original sharer" : "Дељење није успело, зато што је корисник %s већ оригинални делилац", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Дељење %s није успело зато што дозволе превазилазе дозволе гарантоване за %s", "Sharing %s failed, because resharing is not allowed" : "Дељење %s није успело зато што даље дељење није дозвољено", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Дељење %s није успело зато што позадина дељења за %s није могла да нађе извор", "Sharing %s failed, because the file could not be found in the file cache" : "Дељење %s није успело зато што фајл није нађен у кешу фајлова", "Can’t increase permissions of %s" : "Не могу да повећам дозволе за %s", "Files can’t be shared with delete permissions" : "Фајлови не могу бити дељени са дозволама за брисање", "Files can’t be shared with create permissions" : "Фајлови не могу бити дељени са дозволама за креирање", "Expiration date is in the past" : "Датум истека је у прошлости", "Can’t set expiration date more than %s days in the future" : "Не могу да поставим датум истека више од %s дана у будућност", "%s shared »%s« with you" : "%s подели „%s“ са вама", "%s shared »%s« with you." : "%s подели „%s“ са вама.", "Click the button below to open it." : "Кликните дугме испод да га отворите.", "Open »%s«" : "Отвори „%s“", "%s via %s" : "%s путем %s", "The requested share does not exist anymore" : "Захтевано дељење више не постоји", "Could not find category \"%s\"" : "Не могу да пронађем категорију „%s“.", "Sunday" : "Недеља", "Monday" : "Понедељак", "Tuesday" : "Уторак", "Wednesday" : "Среда", "Thursday" : "Четвртак", "Friday" : "Петак", "Saturday" : "Субота", "Sun." : "Нед", "Mon." : "Пон", "Tue." : "Уто", "Wed." : "Сре", "Thu." : "Чет", "Fri." : "Пет", "Sat." : "Суб", "Su" : "Не", "Mo" : "По", "Tu" : "Ут", "We" : "Ср", "Th" : "Че", "Fr" : "Пе", "Sa" : "Су", "January" : "Јануар", "February" : "Фебруар", "March" : "Март", "April" : "Април", "May" : "Мај", "June" : "Јун", "July" : "Јул", "August" : "Август", "September" : "Септембар", "October" : "Октобар", "November" : "Новембар", "December" : "Децембар", "Jan." : "Јан.", "Feb." : "Феб.", "Mar." : "Мар.", "Apr." : "Апр.", "May." : "Мај.", "Jun." : "Јун.", "Jul." : "Јул.", "Aug." : "Авг.", "Sep." : "Сеп.", "Oct." : "Окт.", "Nov." : "Нов.", "Dec." : "Дец.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "У корисничком имену су дозвољени само следећи карактери: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Морате унети исправно корисничко име", "Username contains whitespace at the beginning or at the end" : "Корисничко име садржи белине на почетку или на крају", "Username must not consist of dots only" : "Корисничко име не могу бити само тачке", "A valid password must be provided" : "Морате унети исправну лозинку", "The username is already being used" : "Корисничко име се већ користи", "Could not create user" : "Не могу да направим корисника", "User disabled" : "Корисник онемогућен", "Login canceled by app" : "Пријава отказана од стране апликације", "No app name specified" : "Није наведен назив апликације", "App '%s' could not be installed!" : "Апликација '%s' не може да се инсталира!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Апликација „%s“ не може бити инсталирана јер следеће зависности нису испуњене: %s", "a safe home for all your data" : "сигурно место за све Ваше податке", "File is currently busy, please try again later" : "Фајл је тренутно заузет, покушајте поново касније", "Can't read file" : "Не могу да читам фајл", "Application is not enabled" : "Апликација није укључена", "Authentication error" : "Грешка при провери идентитета", "Token expired. Please reload page." : "Жетон је истекао. Поново учитајте страницу.", "Unknown user" : "Непознат корисник", "No database drivers (sqlite, mysql, or postgresql) installed." : "Нема драјвера базе података (скулајт, мајскул или постгрескул).", "Cannot write into \"config\" directory" : "Не могу уписивати у директоријуму „config“", "Cannot write into \"apps\" directory" : "Не могу уписивати у директоријуму „apps“", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Ово се обично може поправити тако што веб серверу дате приступ уписа за директоријум где су апликације или тако што онемогућите продавницу у config фајлу. Видети %s", "Cannot create \"data\" directory" : "Не могу да направим \"data\" директоријум", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Ово се обично може поправити тако што веб серверу дате право уписа за корени директоријуму. Видети %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Привилегије се обично могу поправити тако што веб серверу дате право уписа за корени директоријуму. Видети %s", "Setting locale to %s failed" : "Постављање локалитета на %s није успело", "Please install one of these locales on your system and restart your webserver." : "Инсталирајте неки од ових локалитета на ваш систем и поново покрените веб сервер.", "Please ask your server administrator to install the module." : "Замолите администратора вашег сервера да инсталира тај модул.", "PHP module %s not installed." : "PHP модул %s није инсталиран.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP поставка „%s“ није постављена на „%s“.", "Adjusting this setting in php.ini will make Nextcloud run again" : "Некстклауд ће прорадити поново када прилагодите ово подашавање у php.ini фајлу", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload је постављено на „%s“ уместо на очекивану вредност „0“", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Да би решили овај проблем поставите <code>mbstring.func_overload</code> на <code>0</code> у фајлу php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Потребан је бар libxml2 2.7.0. Тренутно је инсталиран %s.", "To fix this issue update your libxml2 version and restart your web server." : "Да поправите овај проблем, ажурирајте верзију библиотеке libxml2 и рестартујте веб сервер.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP је очигледно подешен да склања уметнуте doc блокове. То ће учинити неколико кључних апликација недоступним.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Ово је вероватно изазвано кешом или акцелератором као што су ЗендОПкеш или еАкцелератор.", "PHP modules have been installed, but they are still listed as missing?" : "PHP модули су инсталирани али се и даље воде као недостајући?", "Please ask your server administrator to restart the web server." : "Замолите вашег администратора сервера да поново покрене веб сервер.", "PostgreSQL >= 9 required" : "Захтеван је ПостгреСкул >= 9", "Please upgrade your database version" : "Надоградите ваше издање базе", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Промените дозволе у 0770 како директоријуми не би могли бити излистани од стране других корисника.", "Your data directory is readable by other users" : "Директоријум са подацима је читљив од стране других корисника система", "Your data directory must be an absolute path" : "Директоријум са подацима мора бити апсолутна путања", "Check the value of \"datadirectory\" in your configuration" : "Проверите податак за \"datadirectory\" у вашој конфигурацији", "Your data directory is invalid" : "Директоријум са подацима није исправан", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Уверите се да фајл \".ocdata\" постоји у корену директоријума са подацима.", "Could not obtain lock type %d on \"%s\"." : "Не могу да остварим закључаност %d за „%s“.", "Storage unauthorized. %s" : "Складиште није овлашћено. %s", "Storage incomplete configuration. %s" : "Непотпуна конфигурација складишта. %s", "Storage connection error. %s" : "Грешка приликом повезивања на складиште. %s", "Storage is temporarily not available" : "Складиште привремено није доступно", "Storage connection timeout. %s" : "Прекорачено време за повезивање на складиште. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Ово се обично може средити %sдавањем права веб серверу да пише у директоријум са подешавањима%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модул са ИД %s не постоји. Укључите га у поставкама апликација или контактирајте администратора.", "Server settings" : "Подешавања сервера", "DB Error: \"%s\"" : "Грешка базе података: \"%s\"", "Offending command was: \"%s\"" : "Неисправна команда је: „%s“", "You need to enter either an existing account or the administrator." : "Потребно је да унесете или постојећи налог или администраторски.", "Offending command was: \"%s\", name: %s, password: %s" : "Неисправна команда је: „%s“, назив: %s, лозинка: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Постављање дозвола за %s није успело зато што дозволе превазилазе дозволе гарантоване за %s", "Setting permissions for %s failed, because the item was not found" : "Постављање дозвола за %s није успело зато што ставка није пронађена", "Cannot clear expiration date. Shares are required to have an expiration date." : "Не могу обрисати датум трајања. Дељења су у обавези да имају ограничен датум трајања.", "Cannot increase permissions of %s" : "Не могу да повећам привилегије за %s", "Files can't be shared with delete permissions" : "Фајлови не могу бити дељени са привилегијама за брисање", "Files can't be shared with create permissions" : "Фајлови не могу бити дељени са привилегијама за прављење", "Cannot set expiration date more than %s days in the future" : "Датум истека не може да се постави више од %s дана у будућност", "Personal" : "Лично", "Admin" : "Администрација", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Ово се обично може поправити %sgдавањем права уписа веб серверу директоријум%s апликација или искуључивањем продавнице апликација у фајлу config file.", "Cannot create \"data\" directory (%s)" : "Не могу формирати \"data\" директоријуме (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Ово је обично може поправити <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">давајући веб серверу право писања у корени директоријум</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Дозволе се обично могу поправити %sдавањем права уписивања веб серверу основни директоријум%s.", "Data directory (%s) is readable by other users" : "Директоријум података (%s) могу читати остали корисници", "Data directory (%s) must be an absolute path" : "Директоријум података (%s) мора бити апсолутна путања", "Data directory (%s) is invalid" : "Директоријум података (%s) није исправан", "Please check that the data directory contains a file \".ocdata\" in its root." : "Проверите да ли директоријум података садржи фајл „.ocdata“ у свом основном директоријуму." }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); l10n/pt_BR.json 0000604 00000056363 15247130447 0007237 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Não é possível gravar no diretório \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Isso geralmente pode ser corrigido dando o acesso de escritura ao webserver para o diretório de configuração", "See %s" : "Ver %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Normalmente isso pode ser resolvido dando ao webserver permissão de escritura no diretório config. Veja %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Os arquivos do aplicativo %$1s não foram substituídos corretamente. Certifique-se de que é uma versão compatível com o servidor.", "Sample configuration detected" : "Configuração de exemplo detectada", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Foi detectado que a configuração de exemplo foi copiada. Isso pode terminar sua instalação e não é suportado. Por favor leia a documentação antes de realizar mudanças no config.php", "%1$s and %2$s" : "%1$s e %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s e %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s e %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s e %5$s", "Education Edition" : "Edição Educativa", "Enterprise bundle" : "Pacote Enterprise", "Groupware bundle" : "Pacote Groupware", "Social sharing bundle" : "Pacote de compartilhamento social", "PHP %s or higher is required." : "PHP %s ou superior é requerido.", "PHP with a version lower than %s is required." : "É requerida uma versão PHP mais antiga que a %s .", "%sbit or higher PHP required." : "%sbit ou PHP maior é requerido.", "Following databases are supported: %s" : "Os seguintes bancos de dados são suportados: %s", "The command line tool %s could not be found" : "A ferramenta de linha de comando %s não pôde ser encontrada", "The library %s is not available." : "A biblioteca %s não está disponível.", "Library %s with a version higher than %s is required - available version %s." : "É requerida uma biblioteca %s com uma versão maior que %s - versão disponível %s.", "Library %s with a version lower than %s is required - available version %s." : "É requerida uma biblioteca %s com uma versão menor que %s - versão disponível %s.", "Following platforms are supported: %s" : "As seguintes plataformas são suportadas: %s", "Server version %s or higher is required." : "É requerido um servidor da versão %s ou superior.", "Server version %s or lower is required." : "É requerido um servidor da versão %s ou abaixo.", "Unknown filetype" : "Tipo de arquivo desconhecido", "Invalid image" : "Imagem inválida", "Avatar image is not square" : "A imagem do avatar não é quadrada", "today" : "hoje", "yesterday" : "ontem", "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás"], "last month" : "último mês", "_%n month ago_::_%n months ago_" : ["há %n mês atrás","há %n meses atrás"], "last year" : "último ano", "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás"], "_%n hour ago_::_%n hours ago_" : ["há %n hora atrás","há %n horas atrás"], "_%n minute ago_::_%n minutes ago_" : ["há %n minuto atrás","há %n minutos atrás"], "seconds ago" : "segundos atrás", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "O módulo com a ID: %s não existe. Por favor, habilite-o nas configurações de seu aplicativo ou contacte o administrador.", "File name is a reserved word" : "O nome do arquivo é uma palavra reservada", "File name contains at least one invalid character" : "O nome do arquivo contém pelo menos um caracter inválido", "File name is too long" : "O nome do arquivo é muito longo", "Dot files are not allowed" : "Arquivos Dot não são permitidos", "Empty filename is not allowed" : "Nome vazio para arquivo não é permitido.", "App \"%s\" cannot be installed because appinfo file cannot be read." : "O aplicativo \"%s\" não pode ser instalado pois o arquivo appinfo não pôde ser lido.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "O aplicativo \"%s\" não pode ser instalado pois não é compatível com a versão do servidor.", "This is an automatically sent email, please do not reply." : "Este é um e-mail enviado automaticamente. Por favor, não responda.", "Help" : "Ajuda", "Apps" : "Aplicativos", "Settings" : "Configurações", "Log out" : "Sair", "Users" : "Usuários", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Configurações básicas", "Sharing" : "Compartilhamento", "Security" : "Segurança", "Encryption" : "Criptografia", "Additional settings" : "Configurações adicionais", "Tips & tricks" : "Dicas & truques", "Personal info" : "Informação Pessoal", "Sync clients" : "Clientes de sincronização", "Unlimited" : "Ilimitado", "__language_name__" : "__language_name__", "Verifying" : "Verificando", "Verifying …" : "Verificando...", "Verify" : "Verificar", "%s enter the database username and name." : "%s insira o nome de usuário e o nome do banco de dados.", "%s enter the database username." : "%s insira o nome de usuário do banco de dados.", "%s enter the database name." : "%s insira o nome do banco de dados.", "%s you may not use dots in the database name" : "%s você não pode usar pontos no nome do banco de dados", "Oracle connection could not be established" : "Conexão Oracle não pôde ser estabelecida", "Oracle username and/or password not valid" : "Nome de usuário e/ou senha Oracle inválidos", "PostgreSQL username and/or password not valid" : "Nome de usuário e/ou senha PostgreSQL inválidos", "You need to enter details of an existing account." : "Você necessita entrar detalhes de uma conta existente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X não é suportado e %s não funcionará corretamente nesta plataforma. Use-o por sua conta e risco!", "For the best results, please consider using a GNU/Linux server instead." : "Para obter melhores resultados, por favor considere o uso de um servidor GNU/Linux em seu lugar.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Aparentemente a instância %s está rodando em um ambiente PHP de 32 bits e o open_basedir foi configurado no php.ini. Isto pode gerar problemas com arquivos maiores que 4GB e é altamente não recomendável.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor, remova a configuração de open_basedir de seu php.ini ou mude o PHP para 64bit.", "Set an admin username." : "Defina um nome do usuário administrador.", "Set an admin password." : "Defina uma senha para o administrador.", "Can't create or write into the data directory %s" : "Não foi possível criar ou gravar no diretório de dados %s", "Invalid Federated Cloud ID" : "ID inválida de Nuvem Federada", "Sharing %s failed, because the backend does not allow shares from type %i" : "O compartilhamento %s falhou pois a plataforma de serviço não permite ações de tipo %i", "Sharing %s failed, because the file does not exist" : "Compartilhamento %s falhou pois o arquivo não existe", "You are not allowed to share %s" : "Você não tem permissão para compartilhar %s", "Sharing %s failed, because you can not share with yourself" : "O compartilhamento %s falhou pois você não pode compartilhar com você mesmo", "Sharing %s failed, because the user %s does not exist" : "O compartilhamento %s falhou pois o usuário %s não existe", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "O compartilhamento %s falhou pois o usuário %s não é membro de nenhum grupo que o usuário %s pertença", "Sharing %s failed, because this item is already shared with %s" : "O compartilhamento %s falhou pois este ítem já está compartilhado com %s", "Sharing %s failed, because this item is already shared with user %s" : "O compartilhamento de %s falhou pois esse item já é compartilhada com o usuário %s", "Sharing %s failed, because the group %s does not exist" : "O compartilhamento %s falhou pois o grupo %s não existe", "Sharing %s failed, because %s is not a member of the group %s" : "O compartilhamento %s falhou, pois %s não é membro do grupo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Você precisa fornecer uma senha para criar um link público, apenas links protegidos são permitidos", "Sharing %s failed, because sharing with links is not allowed" : "O compartilhamento %s falhou pois compartilhamento com links não é permitido", "Not allowed to create a federated share with the same user" : "Não é permitido criar um compartilhamento associado com o mesmo usuário", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "O compartilhamento %s falhou pois não foi possível encontrar %s. Talvez o servidor esteja inacessível.", "Share type %s is not valid for %s" : "O tipo de compartilhamento %s não é válido para %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Não foi possível definir a data de expiração. Os compartilhamentos não podem expirar mais tarde que %s depois de terem sido compartilhados", "Cannot set expiration date. Expiration date is in the past" : "Não foi possível definir a data de expiração pois ela está no passado", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "A plataforma de compartilhamento %s deve implementar a interface OCP\\Share_Backend", "Sharing backend %s not found" : "Plataforma de serviço de compartilhamento %s não encontrada", "Sharing backend for %s not found" : "Plataforma de compartilhamento para %s não foi encontrada", "Sharing failed, because the user %s is the original sharer" : "O compartilhamento falhou pois o usuário %s é o compartilhador original", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Compartilhamento %s falhou pois as permissões excedem as permissões concedidas a %s", "Sharing %s failed, because resharing is not allowed" : "O compartilhamento %s falhou pois recompartilhamentos não são permitidos", "Sharing %s failed, because the sharing backend for %s could not find its source" : "O compartilhamento %s falhou pois a plataforma de serviço de compartilhamento para %s não conseguiu encontrar a sua fonte", "Sharing %s failed, because the file could not be found in the file cache" : "O compartilhamento %s falhou pois o arquivo não pôde ser encontrado no cache de arquivos", "Can’t increase permissions of %s" : "Não posso aumentar as permissões de %s", "Files can’t be shared with delete permissions" : "Os arquivos não podem ser compartilhados com permissões de exclusão", "Files can’t be shared with create permissions" : "Os arquivos não podem ser compartilhados com permissões de criação", "Expiration date is in the past" : "Data de expiração está no passado", "Can’t set expiration date more than %s days in the future" : "Não é possível definir a expiração mais do que %s dias no futuro", "%s shared »%s« with you" : "%s compartilhou »%s« com você", "%s shared »%s« with you." : "%s compartilhou »%s« com você.", "Click the button below to open it." : "Clique no botão abaixo para abri-lo.", "Open »%s«" : "Abrir »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "O compartilhamento solicitado não existe mais", "Could not find category \"%s\"" : "Impossível localizar a categoria \"%s\"", "Sunday" : "Domingo", "Monday" : "Segunda-feira", "Tuesday" : "Terça-feira", "Wednesday" : "Quarta-feira", "Thursday" : "Quinta-feira", "Friday" : "Sexta-feira", "Saturday" : "Sábado", "Sun." : "Dom.", "Mon." : "Seg.", "Tue." : "Ter.", "Wed." : "Qua.", "Thu." : "Qui.", "Fri." : "Sex.", "Sat." : "Sab.", "Su" : "Su", "Mo" : "Se", "Tu" : "Te", "We" : "Qu", "Th" : "Qu", "Fr" : "Se", "Sa" : "Sa", "January" : "Janeiro", "February" : "Fevereiro", "March" : "Março", "April" : "Abril", "May" : "Maio", "June" : "Junho", "July" : "Julho", "August" : "Agosto", "September" : "Setembro", "October" : "Outubro", "November" : "Novembro", "December" : "Dezembro", "Jan." : "Jan.", "Feb." : "Fev.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "Mai.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Ago.", "Sep." : "Set.", "Oct." : "Out.", "Nov." : "Nov.", "Dec." : "Dez.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Somente os seguintes caracteres são permitidos em um nome de usuário: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-'\"", "A valid username must be provided" : "Um nome de usuário válido deve ser fornecido", "Username contains whitespace at the beginning or at the end" : "O nome de usuário contém espaço em branco no início ou no fim", "Username must not consist of dots only" : "Nome do usuário não pode consistir de pontos somente", "A valid password must be provided" : "Uma senha válida deve ser fornecida", "The username is already being used" : "Este nome de usuário já está em uso", "Could not create user" : "Não foi possível criar o usuário", "User disabled" : "Usuário desativado", "Login canceled by app" : "Login cancelado pelo aplicativo", "No app name specified" : "O nome do aplicativo não foi especificado.", "App '%s' could not be installed!" : "O aplicativo '%s' não pôde ser instalado!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "O aplicativo \"%s\" não pode ser instalado pois as seguintes dependências não foram cumpridas: %s", "a safe home for all your data" : "Um lar seguro para todos os seus dados", "File is currently busy, please try again later" : "O arquivo está ocupado, tente novamente mais tarde", "Can't read file" : "Não foi possível ler arquivo", "Application is not enabled" : "O aplicativo não está habilitado", "Authentication error" : "Erro de autenticação", "Token expired. Please reload page." : "O token expirou. Por favor recarregue a página.", "Unknown user" : "Usuário desconhecido", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nenhum driver de banco de dados (sqlite, mysql ou postgresql) instalado.", "Cannot write into \"config\" directory" : "Não foi possível gravar no diretório \"config\"", "Cannot write into \"apps\" directory" : "Não foi possível gravar no diretório \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Normalmente isso pode ser resolvido dando ao webserver permissão de escrita no diretório apps ou desabilitando a appstore no arquivo de configuração. Veja %s", "Cannot create \"data\" directory" : "Não foi possível criar o diretório \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Normalmente isso pode ser resolvido dando ao webserver permissão de escrita no diretório raiz. Veja %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "As permissões normalmente podem ser corrigidas dando permissão de escrita do diretório raiz para o servidor web. Veja %s.", "Setting locale to %s failed" : "Falha ao configurar localização para %s", "Please install one of these locales on your system and restart your webserver." : "Por favor, defina uma dessas localizações em seu sistema e reinicie o seu servidor web.", "Please ask your server administrator to install the module." : "Por favor, peça ao seu administrador do servidor para instalar o módulo.", "PHP module %s not installed." : "Módulo PHP %s não instalado.", "PHP setting \"%s\" is not set to \"%s\"." : "Configuração PHP \"%s\" não está configurado para \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ajustar a configuração no php.ini fará com que o Nextcloud execute novamente", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload está definido para \"%s\" ao invés do valor esperado \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Para corrigir esse problema defina <code>mbstring.func_overload</code> para <code>0</code> em seu php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "A libxml2 2.7.0 é a versão mínima requerida. Atualmente a versão %s está instalada.", "To fix this issue update your libxml2 version and restart your web server." : "Para corrigir este problema, atualize a versão da sua libxml2 e reinicie seu servidor web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP aparentemente está configurado para retirar blocos doc inline. Isso fará com que vários aplicativos do núcleo fiquem inacessíveis.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Isso provavelmente é causado por um cache/acelerador, como Zend OPcache ou eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Módulos do PHP foram instalados, mas eles ainda estão listados como faltantes?", "Please ask your server administrator to restart the web server." : "Por favor peça ao administrador do servidor para reiniciar o servidor web.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 requirido", "Please upgrade your database version" : "Por favor atualize sua versão do banco de dados", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor altere as permissões para 0770 para que o diretório não possa ser lido por outros usuários.", "Your data directory is readable by other users" : "O diretório de dados está legível para outros usuários", "Your data directory must be an absolute path" : "O diretório de dados deve ser um caminho absoluto", "Check the value of \"datadirectory\" in your configuration" : "Verifique o valor do \"datadirectory\" na sua configuração", "Your data directory is invalid" : "Seu diretório de dados é inválido", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Assegure-se que exista um arquivo chamado \".ocdata\" na raiz do diretório \"data\".", "Could not obtain lock type %d on \"%s\"." : "Não foi possível obter tipo de bloqueio %d em \"%s\".", "Storage unauthorized. %s" : "Armazenamento não autorizado. %s", "Storage incomplete configuration. %s" : "Configuração incompleta do armazenamento. %s", "Storage connection error. %s" : "Erro na conexão de armazenamento. %s", "Storage is temporarily not available" : "Armazenamento temporariamente indisponível", "Storage connection timeout. %s" : "Esgotado o tempo de conexão ao armazenamento. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Isso geralmente pode ser corrigido por %s dar a permissão de gravação ao servidor web para o diretório de configuração %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "O módulo com ID: %s não existe. Ative-o em suas configurações de aplicativos ou contacte o administrador.", "Server settings" : "Configurações do servidor", "DB Error: \"%s\"" : "Erro no BD: \"%s\"", "Offending command was: \"%s\"" : "Comando ofensivo era: \"%s\"", "You need to enter either an existing account or the administrator." : "Você precisa inserir uma conta existente ou a conta do administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando ofensivo era: \"%s\", nome: %s, senha: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "A definição de permissões para %s falhou pois as permissões excedem as permissões concedidas a %s", "Setting permissions for %s failed, because the item was not found" : "A definição de permissões para %s falhou pois o item não foi encontrado", "Cannot clear expiration date. Shares are required to have an expiration date." : "Não foi possível eliminar a data de expiração. Compartilhamentos devem ter uma data de expiração.", "Cannot increase permissions of %s" : "Não foi possível aumentar as permissões de %s", "Files can't be shared with delete permissions" : "Os arquivos não podem ser compartilhadas com permissões de exclusão", "Files can't be shared with create permissions" : "Os arquivos não podem ser compartilhados com permissões de criação", "Cannot set expiration date more than %s days in the future" : "Não foi possível definir a data de expiração para mais que %s dias no futuro", "Personal" : "Pessoal", "Admin" : "Admininistrador", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Isto pode ser corrigido por %sdando ao servidor web permissão de escrita para o diretório app%s ou desabilitando o appstore no arquivo de configuração.", "Cannot create \"data\" directory (%s)" : "Não pôde ser criado o diretório \"dados\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Isto geralmente pode ser corrigido ao <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">dar permissão de gravação no diretório raiz</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Permissões podem ser corrigidas por %sdando permissão de escrita ao servidor web para o diretório raiz %s", "Data directory (%s) is readable by other users" : "Diretório de dados (%s) pode ser lido por outros usuários", "Data directory (%s) must be an absolute path" : "Diretório de dados (%s) deve ser um caminho absoluto", "Data directory (%s) is invalid" : "Diretório de dados (%s) é inválido", "Please check that the data directory contains a file \".ocdata\" in its root." : "Por favor, verifique se o diretório de dados contém um arquivo \".ocdata\" em sua raiz." },"pluralForm" :"nplurals=2; plural=(n > 1);" } l10n/hu.json 0000604 00000050340 15247130447 0006632 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Nem írható a \"config\" könyvtár!", "This can usually be fixed by giving the webserver write access to the config directory" : "Ez rendszerint úgy oldható meg, hogy írási jogot adunk a webszervernek a config könyvtárra.", "See %s" : "Lásd %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ez rendszerint úgy oldható meg, hogy írási jogot adunk a webszervernek a config könyvtárra. Lásd: %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "%$1s alkalmazás fájljai nem megfelelően lettek cserélve. Győződj meg róla, hogy ez a verzió kompatibilis-e a szerverrel.", "Sample configuration detected" : "A példabeállítások vannak beállítva", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Úgy tűnik a példakonfigurációt próbálja ténylegesen használni. Ez nem támogatott, és működésképtelenné teheti a telepítést. Kérlek olvasd el a dokumentációt és azt követően változtas a config.php-n!", "%1$s and %2$s" : "%1$s és %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s és %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s és %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s és %5$s", "Education Edition" : "Oktatási verzió", "Enterprise bundle" : "Vállalati csomag", "Groupware bundle" : "Csoportmunka csomag", "Social sharing bundle" : "Közösségi megosztás csomag", "PHP %s or higher is required." : "PHP %s vagy ennél újabb szükséges.", "PHP with a version lower than %s is required." : "Ennél régebbi PHP szükséges: %s.", "%sbit or higher PHP required." : "%sbites vagy újabb PHP szükséges.", "Following databases are supported: %s" : "A következő adatbázisok támogatottak: %s", "The command line tool %s could not be found" : "A parancssori eszköz nem található: %s", "The library %s is not available." : "A könyvtár %s nem áll rendelkezésre.", "Library %s with a version higher than %s is required - available version %s." : "%s könyvtár %s vagy újabb verziója szükséges - elérhető verzió: %s.", "Library %s with a version lower than %s is required - available version %s." : "%s könyvtár %s vagy régebbi verziója szükséges - elérhető verzió: %s.", "Following platforms are supported: %s" : "Ezek a platformok támogatottak: %s", "Server version %s or higher is required." : "%s vagy újabb szerver verzió szükséges.", "Server version %s or lower is required." : "%s vagy régebbi szerver verzió szükséges.", "Unknown filetype" : "Ismeretlen fájl típus", "Invalid image" : "Hibás kép", "Avatar image is not square" : "Az avatár kép nem négyzetes.", "today" : "ma", "yesterday" : "tegnap", "_%n day ago_::_%n days ago_" : ["%n napja","%n napja"], "last month" : "múlt hónapban", "_%n month ago_::_%n months ago_" : ["%n hónapja","%n hónapja"], "last year" : "tavaly", "_%n year ago_::_%n years ago_" : ["%n éve","%n éve"], "_%n hour ago_::_%n hours ago_" : ["%n órája","%n órája"], "_%n minute ago_::_%n minutes ago_" : ["%n perce","%n perce"], "seconds ago" : "pár másodperce", "File name is a reserved word" : "A fajl neve egy rezervált szó", "File name contains at least one invalid character" : "A fájlnév legalább egy érvénytelen karaktert tartalmaz!", "File name is too long" : "A fájlnév túl hosszú!", "Dot files are not allowed" : "Pontozott fájlok nem engedétlyezettek", "Empty filename is not allowed" : "Üres fájlnév nem engedétlyezett", "App \"%s\" cannot be installed because appinfo file cannot be read." : "\"%s\" alkalmazás nem lehet telepíteni, mert az appinfo fájl nem olvasható.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "\"%s\" alkalmazás nem lehet telepíteni, mert nem kompatibilis a szerver jelen verziójával.", "Help" : "Súgó", "Apps" : "Alkalmazások", "Settings" : "Beállítások", "Log out" : "Kijelentkezés", "Users" : "Felhasználók", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Alapvető beállítások", "Sharing" : "Megosztás", "Security" : "Biztonság", "Encryption" : "Titkosítás", "Additional settings" : "További beállítások", "Tips & tricks" : "Tippek és trükkök", "Verifying …" : "Ellenőrzés...", "Verify" : "Ellenőrzés", "%s enter the database username and name." : "%s add meg az adatbázis nevét és felhasználónevét", "%s enter the database username." : "%s adja meg az adatbázist elérő felhasználó login nevét.", "%s enter the database name." : "%s adja meg az adatbázis nevét.", "%s you may not use dots in the database name" : "%s az adatbázis neve nem tartalmazhat pontot", "Oracle connection could not be established" : "Az Oracle kapcsolat nem hozható létre", "Oracle username and/or password not valid" : "Az Oracle felhasználói név és/vagy jelszó érvénytelen", "PostgreSQL username and/or password not valid" : "A PostgreSQL felhasználói név és/vagy jelszó érvénytelen", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "A Mac OS X nem támogatott és %s nem lesz teljesen működőképes. Csak saját felelősségre használja!", "For the best results, please consider using a GNU/Linux server instead." : "A legjobb eredmény érdekében érdemes GNU/Linux-alapú szervert használni.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Úgy tűnik, hogy ez a %s példány 32-bites PHP környezetben fut és az open_basedir konfigurálva van a php.ini fájlban. Ez 4 GB-nál nagyobb fájlok esetén problémákat okozhat így erősen ellenjavallt.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Kérlek távolítsd el az open_basedir beállítást a php.ini-ből, vagy válts 64bit-es PHP-ra.", "Set an admin username." : "Állítson be egy felhasználói nevet az adminisztrációhoz.", "Set an admin password." : "Állítson be egy jelszót az adminisztrációhoz.", "Can't create or write into the data directory %s" : "Nem sikerült létrehozni vagy irni a \"data\" könyvtárba %s", "Invalid Federated Cloud ID" : "Érvénytelen Egyesített Felhő Azonosító", "Sharing %s failed, because the backend does not allow shares from type %i" : "%s megosztása sikertelen, mert a megosztási alrendszer nem engedi a %l típus megosztását", "Sharing %s failed, because the file does not exist" : "%s megosztása sikertelen, mert a fájl nem létezik", "You are not allowed to share %s" : "Nincs jogosultságod %s megosztására", "Sharing %s failed, because you can not share with yourself" : "%s megosztása sikertelen, mert magaddal nem oszthatod meg", "Sharing %s failed, because the user %s does not exist" : "%s megosztása nem sikerült, mert %s felhasználó nem létezik", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s megosztása nem sikerült, mert %s felhasználó nem tagja egyik olyan csoportnak sem, aminek %s tagja", "Sharing %s failed, because this item is already shared with %s" : "%s megosztása nem sikerült, mert ez már meg van osztva %s-vel", "Sharing %s failed, because this item is already shared with user %s" : "%s megosztása sikertelen, mert már meg van osztva %s felhasználóval", "Sharing %s failed, because the group %s does not exist" : "%s megosztása nem sikerült, mert %s csoport nem létezik", "Sharing %s failed, because %s is not a member of the group %s" : "%s megosztása nem sikerült, mert %s felhasználó nem tagja a %s csoportnak", "You need to provide a password to create a public link, only protected links are allowed" : "Meg kell adnia egy jelszót is, mert a nyilvános hivatkozások csak jelszóval védetten használhatók", "Sharing %s failed, because sharing with links is not allowed" : "%s megosztása nem sikerült, mert a hivatkozással történő megosztás nincs engedélyezve", "Not allowed to create a federated share with the same user" : "Azonos felhasználóval nem lehet létrehozni egyesített megosztást.", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s megosztása sikertelen, mert %s nem található, talán a szerver jelenleg nem elérhető.", "Share type %s is not valid for %s" : "A %s megosztási típus nem érvényes %s-re", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Nem lehet beállítani a lejárati időt. A megosztások legfeljebb ennyi idővel járhatnak le a létrehozásukat követően: %s", "Cannot set expiration date. Expiration date is in the past" : "Nem lehet beállítani a lejárati időt, mivel a megadott lejárati időpont már elmúlt.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Az %s megosztási alrendszernek támogatnia kell az OCP\\Share_Backend interface-t", "Sharing backend %s not found" : "A %s megosztási alrendszer nem található", "Sharing backend for %s not found" : "%s megosztási alrendszere nem található", "Sharing failed, because the user %s is the original sharer" : "Megosztás sikertelen, mert %s felhasználó az eredeti megosztó", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "%s megosztása nem sikerült, mert a jogosultságok túllépik azt, ami %s rendelkezésére áll", "Sharing %s failed, because resharing is not allowed" : "%s megosztása nem sikerült, mert a megosztás továbbadása nincs engedélyezve", "Sharing %s failed, because the sharing backend for %s could not find its source" : "%s megosztása nem sikerült, mert %s megosztási alrendszere nem találja", "Sharing %s failed, because the file could not be found in the file cache" : "%s megosztása nem sikerült, mert a fájl nem található a gyorsítótárban", "Expiration date is in the past" : "A lejárati dátum már elmúlt", "%s shared »%s« with you" : "%s megosztotta veled ezt: »%s«", "%s via %s" : "%s - %s", "Could not find category \"%s\"" : "Ez a kategória nem található: \"%s\"", "Sunday" : "Vasárnap", "Monday" : "Hétfő", "Tuesday" : "Kedd", "Wednesday" : "Szerda", "Thursday" : "Csütörtök", "Friday" : "Péntek", "Saturday" : "Szombat", "Sun." : "Vas.", "Mon." : "Hé.", "Tue." : "Ke.", "Wed." : "Sze.", "Thu." : "Csü.", "Fri." : "Pén.", "Sat." : "Szo.", "Su" : "Va", "Mo" : "Hé", "Tu" : "Ke", "We" : "Sze", "Th" : "Cs", "Fr" : "Pé", "Sa" : "Szo", "January" : "Január", "February" : "Február", "March" : "Március", "April" : "Április", "May" : "Május", "June" : "Június", "July" : "Július", "August" : "Augusztus", "September" : "Szeptember", "October" : "Október", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Már.", "Apr." : "Ápr.", "May." : "Máj.", "Jun." : "Jún.", "Jul." : "Júl.", "Aug." : "Aug.", "Sep." : "Szep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "A felhasználónévben csak a következő karakterek engedélyezettek: \"a-z\", \"A-Z\", \"0-9\", és \"_.@-'\"", "A valid username must be provided" : "Érvényes felhasználónevet kell megadnia", "Username contains whitespace at the beginning or at the end" : "A felhasználónév szóközt tartalmaz az elején vagy a végén", "A valid password must be provided" : "Érvényes jelszót kell megadnia", "The username is already being used" : "Ez a bejelentkezési név már foglalt", "User disabled" : "Felhasználó letiltva", "Login canceled by app" : "Bejelentkezés megszakítva az alkalmazás által", "No app name specified" : "Nincs az alkalmazás név megadva.", "App '%s' could not be installed!" : "\"%s\" alkalmazás nem lehet telepíthető!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "\"%s\" alkalmazás nem lehet telepíteni, mert a következő függőségek nincsenek kielégítve: %s", "a safe home for all your data" : "egy biztonságos hely az adataidnak", "File is currently busy, please try again later" : "A fájl jelenleg elfoglalt, kérjük próbáld újra később!", "Can't read file" : "Nem olvasható a fájl", "Application is not enabled" : "Az alkalmazás nincs engedélyezve", "Authentication error" : "Azonosítási hiba", "Token expired. Please reload page." : "A token lejárt. Frissítse az oldalt.", "Unknown user" : "Ismeretlen felhasználó", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nincs telepítve adatbázis-meghajtóprogram (sqlite, mysql vagy postgresql).", "Cannot write into \"config\" directory" : "Nem írható a \"config\" könyvtár", "Cannot write into \"apps\" directory" : "Nem írható az \"apps\" könyvtár", "Setting locale to %s failed" : "A lokalizáció %s-re való állítása nem sikerült", "Please install one of these locales on your system and restart your webserver." : "Kérjük állítsa be a következő lokalizációk valamelyikét a rendszeren és indítsa újra a webszervert!", "Please ask your server administrator to install the module." : "Kérje meg a rendszergazdát, hogy telepítse a modult!", "PHP module %s not installed." : "A %s PHP modul nincs telepítve.", "PHP setting \"%s\" is not set to \"%s\"." : "%s PHP beállítás nincs \"%s\"-re állítva.", "Adjusting this setting in php.ini will make Nextcloud run again" : "A beállítás változtatása a php.ini fájlban újra futtatja a Nexcloud-ot", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload értéke: \"%s\" az elvárt \"0\" helyett", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "A probléma javításához állítsd a <code>mbstring.func_overload</code> értékét <code>0</code>-ra a php.ini fájlban.", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Legalább libxml2 2.7.0 szükséges. Jelenleg telepített: %s", "To fix this issue update your libxml2 version and restart your web server." : "A probléma javításához frissítsd a libxml2 verziót és indítsd újra a webszervert.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Úgy tűnik, hogy a PHP úgy van beállítva, hogy eltávolítja programok belsejében elhelyezett szövegblokkokat. Emiatt a rendszer több alapvető fontosságú eleme működésképtelen lesz.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Ezt valószínűleg egy gyorsítótár ill. kódgyorsító, mint pl, a Zend, OPcache vagy eAccelererator okozza.", "PHP modules have been installed, but they are still listed as missing?" : "A PHP modulok telepítve vannak, de a listában mégsincsenek felsorolva?", "Please ask your server administrator to restart the web server." : "Kérje meg a rendszergazdát, hogy indítsa újra a webszervert!", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 szükséges", "Please upgrade your database version" : "Kérem frissítse az adatbázis-szoftvert!", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Kérjük módosítsa a könyvtár elérhetőségi engedélybeállítását 0770-re, hogy a tartalmát más felhasználó ne listázhassa!", "Check the value of \"datadirectory\" in your configuration" : "Ellenőrizd a \"datadirectory\" értékét a konfigurációban", "Could not obtain lock type %d on \"%s\"." : "Nem sikerült %d típusú zárolást elérni itt: \"%s\".", "Storage unauthorized. %s" : "A tároló jogosulatlan. %s", "Storage incomplete configuration. %s" : "A tároló beállítása nem teljes. %s", "Storage connection error. %s" : "Tároló kapcsolódási hiba. %s", "Storage is temporarily not available" : "A tároló átmenetileg nem érthető el", "Storage connection timeout. %s" : "Tároló kapcsolat időtúllépés. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Ez rendszerint úgy oldható meg, hogy %sírási jogot adunk a webszervernek a config könyvtárra%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "A modul nem létezik, id: %s. Kérlek engedélyezd az alkalmazás beállításoknál vagy keresd az adminisztrátort.", "Server settings" : "Szerver beállítások", "DB Error: \"%s\"" : "Adatbázis hiba: \"%s\"", "Offending command was: \"%s\"" : "A hibát ez a parancs okozta: \"%s\"", "You need to enter either an existing account or the administrator." : "Vagy egy létező felhasználó vagy az adminisztrátor bejelentkezési nevét kell megadnia", "Offending command was: \"%s\", name: %s, password: %s" : "A hibát okozó parancs ez volt: \"%s\", login név: %s, jelszó: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Nem sikerült %s-re beállítani az elérési jogosultságokat, mert a megadottak túllépik a %s-re érvényes jogosultságokat", "Setting permissions for %s failed, because the item was not found" : "Nem sikerült %s-re beállítani az elérési jogosultságokat, mert a kérdéses fájl nem található", "Cannot clear expiration date. Shares are required to have an expiration date." : "Nem lehet beállítani a lejárati időt. A megosztásoknak kötelező megadni lejárati időt!", "Cannot increase permissions of %s" : "%s jogosultságait nem lehet megemelni", "Files can't be shared with delete permissions" : "A fájlokat nem lehet megosztani a törlési jogosultságokkal", "Files can't be shared with create permissions" : "A fájlokat nem lehet megosztani a létrehozási jogosultságokkal", "Cannot set expiration date more than %s days in the future" : "%s napnál távolabbi lejárati dátumot nem lehet beállítani", "Personal" : "Személyes", "Admin" : "Adminisztrátor", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Ez rendszerint úgy oldható meg, hogy %sírási jogot adunk a webszervernek az app könyvtárra%s, vagy letiltjuk a config fájlban az appstore használatát.", "Cannot create \"data\" directory (%s)" : "Nem sikerült létrehozni a \"data\" könyvtárt (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Ez általában úgy javítható, hogy <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">a webszervernek írási jogosultságot adsz a root könyvtárra</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Az elérési problémák rendszerint megoldhatók azzal, ha a %swebszervernek írásjogot adunk a gyökérkönyvtárra%s.", "Data directory (%s) is readable by other users" : "Az adatkönyvtár (%s) más felhasználók számára is olvasható ", "Data directory (%s) must be an absolute path" : "Az adatkönyvtárnak (%s) abszolút elérési útnak kell lennie", "Data directory (%s) is invalid" : "Érvénytelen a megadott adatkönyvtár (%s) ", "Please check that the data directory contains a file \".ocdata\" in its root." : "Kérjük ellenőrizze, hogy az adatkönyvtár tartalmaz a gyökerében egy \".ocdata\" nevű fájlt!" },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/tr.js 0000604 00000054153 15247130447 0006314 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "\"config\" klasörüne yazılamadı!", "This can usually be fixed by giving the webserver write access to the config directory" : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir", "See %s" : "Şuraya bakın: %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir. %s bölümüne bakın", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "%1$s uygulamasının dosyaları doğru şekilde değiştirilmedi. Sunucu ile uyumlu dosyaların yüklü olduğundan emin olun.", "Sample configuration detected" : "Örnek yapılandırma algılandı", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Örnek yapılandırmanın kopyalanmış olabileceği tespit edildi. Bu durum kurulumunuzu bozabilir ve desteklenmez. Lütfen config.php dosyasında değişiklik yapmadan önce belgeleri okuyun", "%1$s and %2$s" : "%1$s ve %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s ve %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s ve %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s ve %5$s", "Education Edition" : "Eğitim Sürümü", "Enterprise bundle" : "Kurumsal paket", "Groupware bundle" : "Grup paketi", "Social sharing bundle" : "Sosyal ağ paketi", "PHP %s or higher is required." : "PHP %s ya da daha sonraki bir sürümü gerekli.", "PHP with a version lower than %s is required." : "PHP %s ya da daha önceki bir sürümü gerekli.", "%sbit or higher PHP required." : "%sbit ya da daha sonraki bir PHP sürümü gerekli.", "Following databases are supported: %s" : "Şu veritabanları destekleniyor: %s", "The command line tool %s could not be found" : "%s komut satırı aracı bulunamadı", "The library %s is not available." : "%s kitaplığı bulunamadı.", "Library %s with a version higher than %s is required - available version %s." : "%s kitaplığının %s sonrası bir sürümü gerekli. Geçerli sürüm: %s.", "Library %s with a version lower than %s is required - available version %s." : "%s kitaplığının %s öncesi bir sürümü gerekli. Geçerli sürüm: %s.", "Following platforms are supported: %s" : "Şu platformlar destekleniyor: %s", "Server version %s or higher is required." : "Sunucu %s ya da daha sonraki bir sürüm olmalıdır.", "Server version %s or lower is required." : "Sunucu %s ya da daha önceki bir sürüm olmalıdır.", "Unknown filetype" : "Dosya türü bilinmiyor", "Invalid image" : "Görsel geçersiz", "Avatar image is not square" : "Avatar görseli kare değil", "today" : "bugün", "yesterday" : "dün", "_%n day ago_::_%n days ago_" : ["%n gün önce","%n gün önce"], "last month" : "geçen ay", "_%n month ago_::_%n months ago_" : ["%n ay önce","%n ay önce"], "last year" : "geçen yıl", "_%n year ago_::_%n years ago_" : ["%n yıl önce","%n yıl önce"], "_%n hour ago_::_%n hours ago_" : ["%n saat önce","%n saat önce"], "_%n minute ago_::_%n minutes ago_" : ["%n dakika önce","%n dakika önce"], "seconds ago" : "saniye önce", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "%s kodlu modül bulunamadı. Lütfen uygulamalarınız içinden modülü etkinleştirin ya da sistem yöneticinizle görüşün.", "File name is a reserved word" : "Bu dosya adı sistem kullanıma ayrılmıştır", "File name contains at least one invalid character" : "Dosya adında en az bir geçersiz karakter var", "File name is too long" : "Dosya adı çok uzun", "Dot files are not allowed" : "Nokta dosyalarına izin verilmiyor", "Empty filename is not allowed" : "Boş dosya adına izin verilmiyor", "App \"%s\" cannot be installed because appinfo file cannot be read." : "appinfo dosyası okunamadığından \"%s\" uygulaması kurulamaz.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "\"%s\" uygulaması sunucu sürümüyle uyumlu olmadığından kurulamaz.", "This is an automatically sent email, please do not reply." : "Bu ileti otomatik olarak gönderildiğinden lütfen yanıtlamayın.", "Help" : "Yardım", "Apps" : "Uygulamalar", "Settings" : "Ayarlar", "Log out" : "Oturumu Kapat", "Users" : "Kullanıcılar", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Temel Ayarlar", "Sharing" : "Paylaşım", "Security" : "Güvenlik", "Encryption" : "Şifreleme", "Additional settings" : "Ek ayarlar", "Tips & tricks" : "İpucu ve kolaylıklar", "Personal info" : "Kişisel Bilgiler", "Sync clients" : "Eşitleme istemcileri", "Unlimited" : "Sınırsız", "__language_name__" : "Türkçe", "Verifying" : "Doğrulanıyor", "Verifying …" : "Doğrulanıyor...", "Verify" : "Doğrula", "%s enter the database username and name." : "%s veritabanı adını ve kullanıcı adını yazın.", "%s enter the database username." : "%s veritabanı kullanıcı adını yazın.", "%s enter the database name." : "%s veritabanı adını yazın.", "%s you may not use dots in the database name" : "%s veritabanı adında nokta kullanamayabilirsiniz", "Oracle connection could not be established" : "Oracle bağlantısı kurulamadı", "Oracle username and/or password not valid" : "Oracle kullanıcı adı ya da parolası geçersiz", "PostgreSQL username and/or password not valid" : "PostgreSQL kullanıcı adı ya da parolası geçersiz", "You need to enter details of an existing account." : "Varolan bir hesabın bilgilerini yazmalısınız.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X desteklenmiyor ve %s bu platformda düzgün çalışmayacak. Kullanmaktan doğacak riskler size aittir!", "For the best results, please consider using a GNU/Linux server instead." : "En iyi sonucu almak için GNU/Linux sunucusu kullanın.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Bu %s kopyasının 32-bit PHP ortamında çalıştırıldığı ve open_basedir seçeneğinin php.ini dosyasından ayarlandığı görülüyor. Bu yapılandırma 4 GB boyutundan büyük dosyalarda sorun çıkarır ve kullanılması önerilmez.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Lütfen php.ini dosyasındaki open_basedir ayarını kaldırın ya da 64-bit PHP sürümüne geçin.", "Set an admin username." : "Bir yönetici kullanıcı adı yazın.", "Set an admin password." : "Bir yönetici parolası yazın.", "Can't create or write into the data directory %s" : "%s veri klasörü oluşturulamadı ya da içine yazılamadı", "Invalid Federated Cloud ID" : "Birleşmiş Bulut Kimliği Geçersiz", "Sharing %s failed, because the backend does not allow shares from type %i" : "Arka uç %s türündeki paylaşımlara izin vermediğinden %s paylaşılamadı", "Sharing %s failed, because the file does not exist" : "Dosya bulunamadığından %s paylaşılamadı", "You are not allowed to share %s" : "%s ögesini paylaşma izniniz yok", "Sharing %s failed, because you can not share with yourself" : "%s paylaşılamadı. Ögeyi kendiniz ile paylaşamazsınız", "Sharing %s failed, because the user %s does not exist" : "%s paylaşılamadı. %s kullanıcısı bulunamadı", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s paylaşılamadı. %s kullanıcısı %s ögesinin üyesi olduğu grupların herhangi birinin üyesi değil", "Sharing %s failed, because this item is already shared with %s" : "%s paylaşılamadı. Bu öge %s ile zaten paylaşılmış", "Sharing %s failed, because this item is already shared with user %s" : "%s paylaşılamadı. Bu öge zaten %s kullanıcısı ile paylaşılmış", "Sharing %s failed, because the group %s does not exist" : "%s paylaşılamadı. %s grubu bulunamadı", "Sharing %s failed, because %s is not a member of the group %s" : "%s paylaşılamadı. %s kullanıcısı %s grubunun üyesi değil", "You need to provide a password to create a public link, only protected links are allowed" : "Herkese açık bir bağlantı oluşturmak için bir parola belirtmelisiniz. Yalnız korunmuş bağlantılara izin verilir", "Sharing %s failed, because sharing with links is not allowed" : "%s paylaşılamadı. Bağlantı üzerinden paylaşım izni verilmiyor", "Not allowed to create a federated share with the same user" : "Aynı kullanıcı ile bir birleşmiş paylaşım oluşturulamaz", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s paylaşılamadı. %s bulunamadı. Sunucuya şu anda erişilemiyor olabilir.", "Share type %s is not valid for %s" : "%s paylaşım türü %s için geçerli değil", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Son kullanma tarihi ayarlanamadı. Paylaşımların kullanım süresi paylaşıldıktan %s sonra dolamaz", "Cannot set expiration date. Expiration date is in the past" : "Son kullanma tarihi ayarlanamıyor. Son kullanma tarihi geçmişte", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Paylaşım arka ucu %s OCP\\Share_Backend arayüzünü desteklemeli", "Sharing backend %s not found" : "%s paylaşım arka ucu bulunamadı", "Sharing backend for %s not found" : "%s için paylaşım arka ucu bulunamadı", "Sharing failed, because the user %s is the original sharer" : "Paylaşılamadı. %s kullanıcısı özgün paylaşan kişi", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "%s paylaşılamadı. İzinler %s için verilen izin düzeyini aşıyor", "Sharing %s failed, because resharing is not allowed" : "%s paylaşılamadı. Yeniden paylaşıma izin verilmiyor", "Sharing %s failed, because the sharing backend for %s could not find its source" : "%s paylaşılamadı. Paylaşım arka ucu %s kaynağını bulamadı", "Sharing %s failed, because the file could not be found in the file cache" : "%s paylaşılamadı. Dosyanın dosya ön belleğinde bulunamadı", "Can’t increase permissions of %s" : "%s izinleri arttırılamadı", "Files can’t be shared with delete permissions" : "Silme izni ile dosya paylaşılamaz", "Files can’t be shared with create permissions" : "Ekleme izni ile dosya paylaşılamaz", "Expiration date is in the past" : "Son kullanma tarihi geçmişte", "Can’t set expiration date more than %s days in the future" : "Son kullanma tarihi %sgünden sonrası olarak ayarlanamaz", "%s shared »%s« with you" : "%s sizinle »%s« ögesini paylaştı", "%s shared »%s« with you." : "%s sizinle »%s« ögesini paylaştı.", "Click the button below to open it." : "Açmak için aşağıdaki düğmeye tıklayın.", "Open »%s«" : "»%s« Aç", "%s via %s" : "%s, %s aracılığıyla", "The requested share does not exist anymore" : "Erişilmek istenilen paylaşım artık yok", "Could not find category \"%s\"" : "\"%s\" kategorisi bulunamadı", "Sunday" : "Pazar", "Monday" : "Pazartesi", "Tuesday" : "Salı", "Wednesday" : "Çarşamba", "Thursday" : "Perşembe", "Friday" : "Cuma", "Saturday" : "Cumartesi", "Sun." : "Paz", "Mon." : "Pzt", "Tue." : "Sal", "Wed." : "Çar", "Thu." : "Per", "Fri." : "Cum", "Sat." : "Cmt", "Su" : "Pa", "Mo" : "Pt", "Tu" : "Sa", "We" : "Ça", "Th" : "Pe", "Fr" : "Cu", "Sa" : "Ct", "January" : "Ocak", "February" : "Şubat", "March" : "Mart", "April" : "Nisan", "May" : "Mayıs", "June" : "Haziran", "July" : "Temmuz", "August" : "Ağustos", "September" : "Eylül", "October" : "Ekim", "November" : "Kası", "December" : "Aralı", "Jan." : "Oca", "Feb." : "Şub", "Mar." : "Mar", "Apr." : "Nis", "May." : "May", "Jun." : "Haz", "Jul." : "Tem", "Aug." : "Ağu", "Sep." : "Eyl", "Oct." : "Eki", "Nov." : "Kas", "Dec." : "Ara", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Kullanıcı adında yalnız şu karakterler kullanılabilir: \"a-z\", \"A-Z\", \"0-9\", ve \"_.@-'\"", "A valid username must be provided" : "Geçerli bir kullanıcı adı yazmalısınız", "Username contains whitespace at the beginning or at the end" : "Kullanıcı adının başı ya da sonunda boşluk var", "Username must not consist of dots only" : "Kullanıcı adı yalnız noktalardan oluşamaz", "A valid password must be provided" : "Geçerli bir parola yazmalısınız", "The username is already being used" : "Bu kullanıcı adı zaten var", "Could not create user" : "Kullanıcı oluşturulamadı", "User disabled" : "Kullanıcı devre dışı", "Login canceled by app" : "Oturum açma uygulama tarafından iptal edildi", "No app name specified" : "Uygulama adı belirtilmemiş", "App '%s' could not be installed!" : "'%s' uygulaması kurulamadı!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "\"%s\" uygulaması, şu gereklilikler sağlanmadığı için kurulamıyor: %s", "a safe home for all your data" : "verileriniz için güvenli bir barınak", "File is currently busy, please try again later" : "Dosya şu anda meşgul, lütfen daha sonra deneyin", "Can't read file" : "Dosya okunamadı", "Application is not enabled" : "Uygulama etkinleştirilmemiş", "Authentication error" : "Kimlik doğrulama sorunu", "Token expired. Please reload page." : "Kodun süresi dolmuş. Lütfen sayfayı yenileyin.", "Unknown user" : "Kullanıcı bilinmiyor", "No database drivers (sqlite, mysql, or postgresql) installed." : "Herhangi bir veritabanı sürücüsü (sqlite, mysql ya da postgresql) kurulmamış.", "Cannot write into \"config\" directory" : "\"config\" klasörüne yazılamıyor", "Cannot write into \"apps\" directory" : "\"apps\" klasörüne yazılamıyor", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Bu sorun genellikle, web sunucusuna apps klasörüne yazma izni verilerek ya da yapılandırma dosyasından uygulama mağazası devre dışı bırakılarak çözülebilir. %s bölümüne bakın", "Cannot create \"data\" directory" : "\"data\" klasörü oluşturulamadı", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Bu sorun genellikle, web sunucusuna kök klasöre yazma izni verilerek çözülebilir. %s bölümüne bakın", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "İzinler genellikle, web sunucusuna kök klasöre yazma izni verilerek düzeltilebilir. %s bölümüne bakın.", "Setting locale to %s failed" : "Dil %s olarak ayarlanamadı", "Please install one of these locales on your system and restart your webserver." : "Lütfen bu dillerden birini sisteminize kurun ve web sunucunuzu yeniden başlatın.", "Please ask your server administrator to install the module." : "Lütfen modülü kurması için sunucu yöneticinizle görüşün.", "PHP module %s not installed." : "PHP %s modülü kurulmamış.", "PHP setting \"%s\" is not set to \"%s\"." : "\"%s\" PHP ayarı \"%s\" olarak ayarlanmamış.", "Adjusting this setting in php.ini will make Nextcloud run again" : "php.ini dosyasında bu ayar yapıldığında Nextcloud yeniden çalışır", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload, beklenen \"0\" değeri yerine \"%s\" olarak ayarlanmış", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bu sorunu çözmek için php.ini dosyasındaki <code>mbstring.func_overload</code> seçeneğini <code>0</code> olarak ayarlayın", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 sürümü en az 2.7.0 olmalıdır. Şu anda %s kurulu.", "To fix this issue update your libxml2 version and restart your web server." : "Bu sorunu çözmek için libxml2 sürümünüzü güncelleyin ve web sunucusunu yeniden başlatın.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP girintili doc bloklarını ayıklamak üzere yapılandırılmış gibi görünüyor. Bu durum bazı çekirdek uygulamalarına erişilmesini engelleyecek.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Bu sorun genellikle Zend OPcache ya da eAccelerator gibi bir ön bellek/hızlandırıcı nedeniyle ortaya çıkar.", "PHP modules have been installed, but they are still listed as missing?" : "PHP modülleri kurulmuş, ancak hala eksik olarak mı görünüyor?", "Please ask your server administrator to restart the web server." : "Lütfen web sunucusunu yeniden başlatması için sunucu yöneticinizle görüşün.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 gerekli", "Please upgrade your database version" : "Lütfen veritabanı sürümünüzü yükseltin", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Lütfen izinleri 0770 olarak ayarlayarak diğer kullanıcıların klasörü görebilmesini sağlayın.", "Your data directory is readable by other users" : "Veri klasörünüz diğer kullanıcılar tarafından okunabilir", "Your data directory must be an absolute path" : "Veri klasörünüz mutlak bir yol olmalıdır", "Check the value of \"datadirectory\" in your configuration" : "Yapılandırmanızdaki \"datadirectory\" seçeneğini denetleyin", "Your data directory is invalid" : "Veri klasörünüz geçersiz", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Veri klasörü kökünde \".ocdata\" adında bir dosya bulunduğundan emin olun.", "Could not obtain lock type %d on \"%s\"." : "\"%s\" için %d kilit türü alınamadı.", "Storage unauthorized. %s" : "Depolamaya erişim izni yok. %s", "Storage incomplete configuration. %s" : "Depolama yapılandırması tamamlanmamış. %s", "Storage connection error. %s" : "Depolama bağlantısı sorunu. %s", "Storage is temporarily not available" : "Depolama geçici olarak kullanılamıyor", "Storage connection timeout. %s" : "Depolama bağlantısı zaman aşımı. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Bu sorun genellikle, %sweb sunucusuna config klasörüne yazma izni verilerek%s çözülebilir.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "%s kodlu modül bulunamadı. Lütfen uygulamalarınız içinden modülü etkinleştirin ya da sistem yöneticinizle görüşün.", "Server settings" : "Sunucu ayarları", "DB Error: \"%s\"" : "Veritabanı Sorunu: \"%s\"", "Offending command was: \"%s\"" : "Saldırgan komut: \"%s\"", "You need to enter either an existing account or the administrator." : "Varolan bir hesap ya da yönetici hesabı yazmalısınız.", "Offending command was: \"%s\", name: %s, password: %s" : "Saldırgan komut: \"%s\", kullanıcı adı: %s, parola: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "%s için izinler ayarlanamadı. İzinler %s için verilmiş izin düzeyini aşıyor", "Setting permissions for %s failed, because the item was not found" : "%s için izinler ayarlanamadı. Öge bulunamadı", "Cannot clear expiration date. Shares are required to have an expiration date." : "Son kullanım tarihi temizlenemiyor. Paylaşımların bir son kullanma tarihi olmalıdır.", "Cannot increase permissions of %s" : "%s izinleri yükseltilemiyor", "Files can't be shared with delete permissions" : "Dosyalar silme izniyle paylaşılamaz", "Files can't be shared with create permissions" : "Dosyalar oluşturma izniyle paylaşılamaz", "Cannot set expiration date more than %s days in the future" : "Paylaşımların son kullanım süreleri, gelecekte %s günden fazla olamaz", "Personal" : "Kişisel", "Admin" : "Yönetici", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Bu sorun genellikle, %sweb sunucusuna apps klasörüne yazma izni verilerek%s çözülebilir.", "Cannot create \"data\" directory (%s)" : "\"Veri\" klasörü oluşturulamadı (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Bu sorun genellikle, <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">web sunucusuna kök klasöre yazma izni verilerek</a> çözülebilir.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "İzinler genellikle, %sweb sunucusuna kök klasöre yazma izni verilerek%s düzeltilebilir.", "Data directory (%s) is readable by other users" : "Veri klasörü (%s) diğer kullanıcılar tarafından okunabilir", "Data directory (%s) must be an absolute path" : "Veri klasörü (%s) mutlak bir yol olmalıdır", "Data directory (%s) is invalid" : "Veri klasörü (%s) geçersiz", "Please check that the data directory contains a file \".ocdata\" in its root." : "Lütfen veri klasörünün kökünde \".ocdata\" dosyasının bulunduğunu denetleyin." }, "nplurals=2; plural=(n > 1);"); l10n/pl.json 0000604 00000056561 15247130447 0006644 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Nie można zapisać do katalogu \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Można to zwykle rozwiązać przez dodanie serwerowi www uprawnień zapisu do katalogu config.", "See %s" : "Zobacz %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Zwykle można to rozwiązać nadając serwerowi www uprawnienia do zapisu w katalogu konfiguracji. Zobacz %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Pliki aplikacji %1$s nie zostały zastąpione prawidłowo. Upewnij się, że to jest wersja kompatybilna z serwerem.", "Sample configuration detected" : "Wykryto przykładową konfigurację", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Wykryto skopiowanie przykładowej konfiguracji. To może popsuć Twoją instalację i nie jest wspierane. Proszę przeczytać dokumentację przed dokonywaniem zmian w config.php", "%1$s and %2$s" : "%1$s i %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s i %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s i %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s i %5$s", "Education Edition" : "Wersja edukacyjna", "Enterprise bundle" : "Zestaw biznesowy", "Groupware bundle" : "Zestaw pracy grupowej", "Social sharing bundle" : "Zestaw współdzielenia społecznościowego", "PHP %s or higher is required." : "PHP %s lub wyższe jest wymagane.", "PHP with a version lower than %s is required." : "Wersja PHP jest niższa niż %s, która jest wymagana.", "%sbit or higher PHP required." : "%sbit lub wyższe PHP jest wymagane.", "Following databases are supported: %s" : "Obsługiwane są następujące bazy danych: %s", "The command line tool %s could not be found" : "Narzędzie konsoli %s nie zostało znalezione", "The library %s is not available." : "Biblioteka %s nie jest dostępna.", "Library %s with a version higher than %s is required - available version %s." : "Biblioteka %s w wersji wyższej niż %s, która jest wymagana - dostępna wersja %s.", "Library %s with a version lower than %s is required - available version %s." : "Biblioteka w wersji %s jest niższa niż %s, która jest wymagana - dostępna wersja %s.", "Following platforms are supported: %s" : "Obsługiwane są następujące platformy: %s", "Server version %s or higher is required." : "Wersja serwera %s lub wyższa jest wymagana.", "Server version %s or lower is required." : "Wersja serwera %s lub niższa jest wymagana.", "Unknown filetype" : "Nieznany typ pliku", "Invalid image" : "Błędne zdjęcie", "Avatar image is not square" : "Obraz awataru nie jest kwadratowy", "today" : "dziś", "yesterday" : "wczoraj", "_%n day ago_::_%n days ago_" : ["%d dzień temu","%n dni temu","%n dni temu","%n dni temu"], "last month" : "w zeszłym miesiącu", "_%n month ago_::_%n months ago_" : ["%n miesiąc temu","%n miesięcy temu","%n miesięcy temu","%n miesięcy temu"], "last year" : "w zeszłym roku", "_%n year ago_::_%n years ago_" : ["%n rok temu","%n lata temu","%n lat temu","%n lat temu"], "_%n hour ago_::_%n hours ago_" : ["%n godzinę temu","%n godzin temu","%n godzin temu","%n godzin temu"], "_%n minute ago_::_%n minutes ago_" : ["%n minute temu","%n minut temu","%n minut temu","%n minut temu"], "seconds ago" : "sekund temu", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł o ID: %s nie istnieje. Proszę włącz go w ustawieniach aplikacji lub skontaktuj się z administratorem.", "File name is a reserved word" : "Nazwa pliku jest zarezerwowana", "File name contains at least one invalid character" : "Nazwa pliku zawiera co najmniej jeden nieprawidłowy znak", "File name is too long" : "Nazwa pliku zbyt długa", "Dot files are not allowed" : "Pliki z kropką są nie dozwolone", "Empty filename is not allowed" : "Pusta nazwa nie jest dozwolona.", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Aplikacja \"%s\" nie może zostać zainstalowana, ponieważ plik informacyjny nie może zostać odczytany.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikacja \"%s\" nie może zostać zainstalowana, ponieważ jest niekompatybilna z obecną wersją serwera.", "This is an automatically sent email, please do not reply." : "To jest automatycznie wysłany e-mail, proszę nie odpowiadać na niego.", "Help" : "Pomoc", "Apps" : "Aplikacje", "Settings" : "Ustawienia", "Log out" : "Wyloguj", "Users" : "Użytkownicy", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Ustawienia podstawowe", "Sharing" : "Udostępnianie", "Security" : "Bepieczeństwo", "Encryption" : "Szyfrowanie", "Additional settings" : "Ustawienia dodatkowe", "Tips & tricks" : "Porady i wskazówki", "Personal info" : "Informacje Osobiste", "Sync clients" : "Synchronizuj z klientami", "Unlimited" : "Nielimitowane", "__language_name__" : "__nazwa_języka__", "Verifying" : "Weryfikacja", "Verifying …" : "Weryfikacja...", "Verify" : "Zweryfikuj", "%s enter the database username and name." : "Podaj nazwę bazy danych i nazwę użytkownika %s", "%s enter the database username." : "Podaj nazwę użytkownika %s", "%s enter the database name." : "Podaj nazwę bazy danych %s", "%s you may not use dots in the database name" : "Nie możesz używać kropek w nazwie bazy danych %s", "Oracle connection could not be established" : "Nie można ustanowić połączenia z bazą Oracle", "Oracle username and/or password not valid" : "Oracle: Nazwa użytkownika i/lub hasło jest niepoprawne", "PostgreSQL username and/or password not valid" : "PostgreSQL: Nazwa użytkownika i/lub hasło jest niepoprawne", "You need to enter details of an existing account." : "Musisz wprowadzić szczegóły istniejącego konta.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie jest wspierany i %s nie będzie działać poprawnie na tej platformie. Używasz na własne ryzyko!", "For the best results, please consider using a GNU/Linux server instead." : "Aby uzyskać najlepsze rezultaty, rozważ w to miejsce użycie serwera GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Wydaje się, że ta %s instancja używa PHP 32-bitowego środowiska i opcja open_basedir została ustawiona w php.ini. Spowoduje to problemy z plikami większymi niż 4 GB i jest wysoce niezalecane.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Proszę usunąć ustawienie open_basedir ze swojego php.ini albo przestaw na PHP 64-bitowe.", "Set an admin username." : "Ustaw nazwę administratora.", "Set an admin password." : "Ustaw hasło administratora.", "Can't create or write into the data directory %s" : "Nie można tworzyć ani zapisywać w katalogu %s", "Invalid Federated Cloud ID" : "Nieprawidłowy ID Stowarzyszonej Chmury", "Sharing %s failed, because the backend does not allow shares from type %i" : "Współdzielenie %s nie udało się, ponieważ backend nie pozwala na współdzielenie takiego typu jak %i.", "Sharing %s failed, because the file does not exist" : "Wspóldzielenie %s nie powiodło się. ponieważ plik nie istnieje", "You are not allowed to share %s" : "Nie masz uprawnień aby udostępnić %s", "Sharing %s failed, because you can not share with yourself" : "Współdzielenie %s nie udało się, ponieważ nie możesz współdzielić sam ze sobą", "Sharing %s failed, because the user %s does not exist" : "Współdzielenie %s nie powiodło się, ponieważ użytkownik %s nie istnieje", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Współdzielenie %s nie powiodło się, ponieważ użytkownik %s nie jest członkiem żadnej grupy której członkiem jest %s", "Sharing %s failed, because this item is already shared with %s" : "Współdzielenie %s nie powiodło się, ponieważ element jest już współdzielony z %s", "Sharing %s failed, because this item is already shared with user %s" : "Współdzielenie %s nie udało się, ponieważ ten obiekt już jest współdzielony z użytkownikiem %s", "Sharing %s failed, because the group %s does not exist" : "Współdzielenie %s nie powiodło się, ponieważ grupa %s nie istnieje", "Sharing %s failed, because %s is not a member of the group %s" : "Współdzielenie %s nie powiodło się, ponieważ %s nie jest członkiem grupy %s", "You need to provide a password to create a public link, only protected links are allowed" : "Musisz zapewnić hasło aby utworzyć link publiczny, dozwolone są tylko linki chronione", "Sharing %s failed, because sharing with links is not allowed" : "Współdzielenie %s nie powiodło się, ponieważ współdzielenie z linkami nie jest dozwolone", "Not allowed to create a federated share with the same user" : "Nie jest dozwolone tworzenie współdzielenia stowarzyszonego z tym samym użytkownikiem", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Współdzielenie %s nie powiodło się, nie można odnaleźć %s. Prawdopobnie serwer nie jest teraz osiągalny.", "Share type %s is not valid for %s" : "Typ udziału %s nie jest właściwy dla %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Nie można ustawić daty wygaśnięcia. Udziały nie mogą wygasać później niż %s od momentu udostępnienia", "Cannot set expiration date. Expiration date is in the past" : "Nie można ustawić daty wygaśnięcia. Data wygaśnięcia jest w przeszłości.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Zaplecze do współdzielenia %s musi implementować interfejs OCP\\Share_Backend", "Sharing backend %s not found" : "Zaplecze %s do współdzielenia nie zostało znalezione", "Sharing backend for %s not found" : "Zaplecze do współdzielenia %s nie zostało znalezione", "Sharing failed, because the user %s is the original sharer" : "Współdzielenie z użytkownikiem %s się nie udało, ponieważ już jest współdzielenie z tym użytkownikiem.", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Współdzielenie %s nie powiodło się, ponieważ uprawnienia przekraczają te udzielone %s", "Sharing %s failed, because resharing is not allowed" : "Współdzielenie %s nie powiodło się, ponieważ ponowne współdzielenie nie jest dozwolone", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Współdzielenie %s nie powiodło się, ponieważ zaplecze współdzielenia dla %s nie mogło znaleźć jego źródła", "Sharing %s failed, because the file could not be found in the file cache" : "Współdzielenie %s nie powiodło się, ponieważ plik nie może zostać odnaleziony w buforze plików", "Can’t increase permissions of %s" : "Nie można zwiększyć praw dla 1%s", "Files can’t be shared with delete permissions" : "Pliki nie mogą zostać udostępnione z prawem do usuwania", "Files can’t be shared with create permissions" : "Pliki nie mogą zostać udostępnione z prawem do tworzenia", "Expiration date is in the past" : "Data ważności jest przeszła", "Can’t set expiration date more than %s days in the future" : "Nie można ustawić daty ważności dłuższej niż 1%s dni", "%s shared »%s« with you" : "%s współdzieli »%s« z tobą", "%s shared »%s« with you." : "%s współdzieli »%s« z Tobą.", "Click the button below to open it." : "Kliknij przycisk poniżej aby otworzyć.", "Open »%s«" : "Otwórz »%s«", "%s via %s" : "%s przez %s", "The requested share does not exist anymore" : "Żądany obiekt współdzielony już nie istnieje", "Could not find category \"%s\"" : "Nie można odnaleźć kategorii \"%s\"", "Sunday" : "Niedziela", "Monday" : "Poniedziałek", "Tuesday" : "Wtorek", "Wednesday" : "Środa", "Thursday" : "Czwartek", "Friday" : "Piątek", "Saturday" : "Sobota", "Sun." : "Nd.", "Mon." : "Pon.", "Tue." : "Wt.", "Wed." : "Śr.", "Thu." : "Czw.", "Fri." : "Pt.", "Sat." : "Sob.", "Su" : "Nd.", "Mo" : "Pon.", "Tu" : "Wt.", "We" : "Śr.", "Th" : "Czw.", "Fr" : "Pt.", "Sa" : "Sob.", "January" : "Styczeń", "February" : "Luty", "March" : "Marzec", "April" : "Kwiecień", "May" : "Maj", "June" : "Czerwiec", "July" : "Lipiec", "August" : "Sierpień", "September" : "Wrzesień", "October" : "Październik", "November" : "Listopad", "December" : "Grudzień", "Jan." : "Sty.", "Feb." : "Lut.", "Mar." : "Mar.", "Apr." : "Kwi.", "May." : "Maj.", "Jun." : "Cze.", "Jul." : "Lip.", "Aug." : "Sie.", "Sep." : "Wrz.", "Oct." : "Paź.", "Nov." : "Lis.", "Dec." : "Gru.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "W nazwie użytkownika dozwolone są tylko następujące znaki : \"a-z\", \"A-Z\", \"0-9\" i \"_.@-'\"", "A valid username must be provided" : "Należy podać prawidłową nazwę użytkownika", "Username contains whitespace at the beginning or at the end" : "Nazwa użytkownika zawiera spację na początku albo na końcu", "Username must not consist of dots only" : "Nazwa użytkownika nie może się składać tylko z kropek", "A valid password must be provided" : "Należy podać prawidłowe hasło", "The username is already being used" : "Ta nazwa użytkownika jest już używana", "Could not create user" : "Nie można utworzyć użytkownika.", "User disabled" : "Użytkownik zablokowany", "Login canceled by app" : "Zalogowanie anulowane przez aplikację", "No app name specified" : "Nie określono nazwy aplikacji", "App '%s' could not be installed!" : "Aplikacja '%s' nie mogła zostać zainstalowana!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Aplikacja \"%s\" nie może zostać zainstalowana, ponieważ następujące zależności nie zostały spełnione: %s", "a safe home for all your data" : "Bezpieczny dom dla twoich danych", "File is currently busy, please try again later" : "Plik jest obecnie niedostępny, proszę spróbować ponownie później", "Can't read file" : "Nie można odczytać pliku.", "Application is not enabled" : "Aplikacja nie jest włączona", "Authentication error" : "Błąd uwierzytelniania", "Token expired. Please reload page." : "Token wygasł. Proszę ponownie załadować stronę.", "Unknown user" : "Nieznany użytkownik", "No database drivers (sqlite, mysql, or postgresql) installed." : "Brak sterowników bazy danych (sqlite, mysql or postgresql).", "Cannot write into \"config\" directory" : "Nie można zapisać do katalogu \"config\"", "Cannot write into \"apps\" directory" : "Nie można zapisać do katalogu \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Zazwyczaj można to naprawić poprzez nadanie serwerowi www uprawnień do zapisu w katalogu aplikacji lub poprzez wyłączenie sklepu aplikacji w pliku konfiugracyjnym. Zobacz %s", "Cannot create \"data\" directory" : "Nie mozna utworzyć katalogu \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Zazwyczaj można to naprawić poprzez nadanie serwerowi www uprawnień do zapisu w katalogu głównym. Zobacz %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Uprawnienia mogą zazwyczaj być naprawione poprzez nadanie serwerowi www uprawnień do zapisu w katalogu głównym. Zobacz %s.", "Setting locale to %s failed" : "Nie udało się zmienić języka na %s", "Please install one of these locales on your system and restart your webserver." : "Proszę zainstalować jedno z poniższych locale w Twoim systemie i uruchomić ponownie serwer www.", "Please ask your server administrator to install the module." : "Proszę poproś administratora serwera aby zainstalował ten moduł.", "PHP module %s not installed." : "Moduł PHP %s nie jest zainstalowany.", "PHP setting \"%s\" is not set to \"%s\"." : "Ustawienie PHP \"%s\" nie jest ustawione na \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Modyfikacja tego w php.ini spowoduje, że Nextcloud ponownie będzie działał", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload jest ustawione na \"%s\" zamiast oczekiwanej wartości \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Aby naprawić ten problem ustaw <code>mbstring.func_overload</code> na <code>0</code> w swoim php.ini.", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Wymagana wersja libxml2 to przynajmniej 2.7.0. Obecnie jest zainstalowana wersja %s.", "To fix this issue update your libxml2 version and restart your web server." : "Aby naprawić ten problem zaktualizuj swoją wersję libxml2 i zrestartuj serwer web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Najwidoczniej PHP jest tak ustawione, aby wycinać bloki wklejonych dokumentów. Może to spowodować, że niektóre wbudowane aplikacje będą niedostępne.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dzieje się tak prawdopodobnie przez cache lub akcelerator taki jak Zend OPcache lub eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Moduły PHP zostały zainstalowane, ale nadal brakuje ich na liście?", "Please ask your server administrator to restart the web server." : "Poproś administratora serwera o restart serwera www.", "PostgreSQL >= 9 required" : "Wymagany PostgreSQL >= 9", "Please upgrade your database version" : "Uaktualnij wersję bazy danych", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Zmień uprawnienia na 0770, żeby ukryć zawartość katalogu przed innymi użytkownikami.", "Your data directory is readable by other users" : "Twój katalog z danymi mogą czytać inni użytkownicy", "Your data directory must be an absolute path" : "Twój katalog z danymi musi być ścieżką absolutną", "Check the value of \"datadirectory\" in your configuration" : "Sprawdź wartość \"datadirectory\" w swojej konfiguracji", "Your data directory is invalid" : "Twój katalog z danymi jest nieprawidłowy", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Upewnij się, że istnieje plik \".ocdata\" w katalogu z danymi, data/", "Could not obtain lock type %d on \"%s\"." : "Nie można uzyskać blokady typu %d na \"%s\".", "Storage unauthorized. %s" : "Magazyn nieautoryzowany. %s", "Storage incomplete configuration. %s" : "Niekompletna konfiguracja magazynu. %s", "Storage connection error. %s" : "Błąd połączenia z magazynem. %s", "Storage is temporarily not available" : "Magazyn jest tymczasowo niedostępny", "Storage connection timeout. %s" : "Limit czasu połączenia do magazynu został przekroczony. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Można to zwykle rozwiązać przez %sdodanie serwerowi www uprawnień zapisu do katalogu config%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł z id: %s nie istnieje. Należy go włączyć w ustawieniach aplikacji lub skontaktować się z administratorem.", "Server settings" : "Ustawienia serwera", "DB Error: \"%s\"" : "Błąd DB: \"%s\"", "Offending command was: \"%s\"" : "Niepoprawna komenda: \"%s\"", "You need to enter either an existing account or the administrator." : "Należy wprowadzić istniejące konto użytkownika lub administratora.", "Offending command was: \"%s\", name: %s, password: %s" : "Niepoprawne polecania: \"%s\", nazwa: %s, hasło: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Ustawienie uprawnień dla %s nie powiodło się, ponieważ uprawnienia wykraczają poza przydzielone %s", "Setting permissions for %s failed, because the item was not found" : "Ustawienie uprawnień dla %s nie powiodło się, ponieważ element nie został znaleziony", "Cannot clear expiration date. Shares are required to have an expiration date." : "Nie można wyczyścić daty wygaśnięcia. Współudziały muszą posiadać datę wygaśnięcia.", "Cannot increase permissions of %s" : "Nie można zwiększyć uprawnienia %s", "Files can't be shared with delete permissions" : "Pliki nie mogą być współdzielone z uprawnieniami kasowania", "Files can't be shared with create permissions" : "Pliki nie mogą być współdzielony z uprawnieniami tworzenia", "Cannot set expiration date more than %s days in the future" : "Nie można utworzyć daty wygaśnięcia na %s dni do przodu", "Personal" : "Osobiste", "Admin" : "Administracja", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Można to zwykle rozwiązać przez %sdodanie serwerowi www uprawnień zapisu do katalogu apps%s lub wyłączenie appstore w pliku konfiguracyjnym.", "Cannot create \"data\" directory (%s)" : "Nie można utworzyć katalogu \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Z reguły to może zostać naprawione <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">poprzez danie serwerowi web praw zapisu do katalogu domowego aplikacji</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Problemy z uprawnieniami można zwykle naprawić przez %sdodanie serwerowi www uprawnień zapisu do katalogu głównego%s.", "Data directory (%s) is readable by other users" : "Katalog danych (%s) jest możliwy do odczytania przez innych użytkowników", "Data directory (%s) must be an absolute path" : "Katalog danych (%s) musi być ścieżką absolutną", "Data directory (%s) is invalid" : "Katalog danych (%s) jest nieprawidłowy", "Please check that the data directory contains a file \".ocdata\" in its root." : "Sprawdź, czy katalog danych zawiera plik \".ocdata\"." },"pluralForm" :"nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);" } l10n/de_DE.js 0000604 00000057471 15247130447 0006635 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Das Schreiben in das „config“-Verzeichnis ist nicht möglich!", "This can usually be fixed by giving the webserver write access to the config directory" : "Dies kann normalerweise repariert werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird", "See %s" : "Siehe %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Die Dateien der App %$1s wurden nicht korrekt ersetzt. Stellen Sie sicher, dass die Version mit dem Server kompatibel ist.", "Sample configuration detected" : "Beispielkonfiguration gefunden", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Es wurde festgestellt, dass die Beispielkonfiguration kopiert wurde. Dies kann Ihre Installation zerstören und wird nicht unterstützt. Bitte die Dokumentation lesen, bevor Änderungen an der config.php vorgenommen werden.", "%1$s and %2$s" : "%1$s und %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s und %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s und %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s und %5$s", "Education Edition" : "Bildungsausgabe", "Enterprise bundle" : "Firmen-Paket", "Groupware bundle" : "Groupware-Paket", "Social sharing bundle" : "Paket für das Teilen in sozialen Medien", "PHP %s or higher is required." : "PHP %s oder höher wird benötigt.", "PHP with a version lower than %s is required." : "PHP wird in einer früheren Version als %s benötigt.", "%sbit or higher PHP required." : "%sbit oder höheres PHP wird benötigt.", "Following databases are supported: %s" : "Die folgenden Datenbanken werden unterstützt: %s", "The command line tool %s could not be found" : "Das Kommandozeilenwerkzeug %s konnte nicht gefunden werden", "The library %s is not available." : "Die Bibliothek %s ist nicht verfügbar.", "Library %s with a version higher than %s is required - available version %s." : "Die Bibliothek %s wird in einer neueren Version als %s benötigt - verfügbare Version ist %s.", "Library %s with a version lower than %s is required - available version %s." : "Die Bibliothek %s wird in einer früheren Version als %s benötigt - verfügbare Version ist %s.", "Following platforms are supported: %s" : "Die folgenden Plattformen werden unterstützt: %s", "Server version %s or higher is required." : "Server Version %s oder höher wird benötigt.", "Server version %s or lower is required." : "Server Version %s oder niedriger wird benötigt.", "Unknown filetype" : "Unbekannter Dateityp", "Invalid image" : "Ungültiges Bild", "Avatar image is not square" : "Benutzerbild ist nicht quadratisch", "today" : "Heute", "yesterday" : "Gestern", "_%n day ago_::_%n days ago_" : ["Vor %n Tag","Vor %n Tagen"], "last month" : "Letzten Monat", "_%n month ago_::_%n months ago_" : ["Vor %n Monat","Vor %n Monaten"], "last year" : "Letztes Jahr", "_%n year ago_::_%n years ago_" : ["Vor %n Jahr","Vor %n Jahren"], "_%n hour ago_::_%n hours ago_" : ["Vor %n Stunde","Vor %n Stunden"], "_%n minute ago_::_%n minutes ago_" : ["Vor %n Minute","Vor %n Minuten"], "seconds ago" : "Gerade eben", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte aktiviere es in deinen Einstellungen oder kontaktiere deinen Administrator.", "File name is a reserved word" : "Der Dateiname ist ein reserviertes Wort", "File name contains at least one invalid character" : "Der Dateiname enthält mindestens ein ungültiges Zeichen", "File name is too long" : "Dateiname ist zu lang", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", "Empty filename is not allowed" : "Ein leerer Dateiname ist nicht erlaubt", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Die Anwendung \"%s\" kann nicht installiert werden, weil die Anwendungsinfodatei nicht gelesen werden kann.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Die App \"%s\" kann nicht installiert werden, da sie mit dieser Serverversion nicht kompatibel ist.", "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.", "Help" : "Hilfe", "Apps" : "Apps", "Settings" : "Einstellungen", "Log out" : "Abmelden", "Users" : "Benutzer", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Grundeinstellungen", "Sharing" : "Teilen", "Security" : "Sicherheit", "Encryption" : "Verschlüsselung", "Additional settings" : "Zusätzliche Einstellungen", "Tips & tricks" : "Tipps & Tricks", "Personal info" : "Persönliche Informationen ", "Sync clients" : " Sync-Clients ", "Unlimited" : "Unbegrenzt", "__language_name__" : " Deutsch (Förmlich: Sie) ", "Verifying" : "Überprüfe", "Verifying …" : " Überprüfe… ", "Verify" : "Überprüfen", "%s enter the database username and name." : "%s geben Sie den Datenbank-Benutzernamen und den Datenbanknamen an.", "%s enter the database username." : "%s geben Sie den Datenbank-Benutzernamen an.", "%s enter the database name." : "%s geben Sie den Datenbanknamen an.", "%s you may not use dots in the database name" : "%s Der Datenbankname darf keine Punkte enthalten", "Oracle connection could not be established" : "Es konnte keine Verbindung zur Oracle-Datenbank hergestellt werden", "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "You need to enter details of an existing account." : "Sie müssen Details von einem existierenden Benutzer einfügen.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", "For the best results, please consider using a GNU/Linux server instead." : "Zur Gewährleistung eines optimalen Betriebs sollte stattdessen ein GNU/Linux-Server verwendet werden.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Es scheint, dass diese %s-Instanz unter einer 32-Bit-PHP-Umgebung läuft und open_basedir in der Datei php.ini konfiguriert worden ist. Von einem solchen Betrieb wird dringend abgeraten, weil es dabei zu Problemen mit Dateien kommt, deren Größe 4 GB übersteigt.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Bitte entfernen Sie die open_basedir-Einstellung in Ihrer php.ini oder wechseln Sie zu 64-Bit-PHP.", "Set an admin username." : "Einen Administrator-Benutzernamen setzen.", "Set an admin password." : "Ein Administrator-Passwort setzen.", "Can't create or write into the data directory %s" : "Das Datenverzeichnis %s kann nicht erstellt oder es kann darin nicht geschrieben werden.", "Invalid Federated Cloud ID" : "Ungültige Federated-Cloud-ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Freigabe von %s fehlgeschlagen, da das Backend die Freigabe vom Typ %i nicht erlaubt.", "Sharing %s failed, because the file does not exist" : "Freigabe von %s fehlgeschlagen, da die Datei nicht existiert", "You are not allowed to share %s" : "Die Freigabe von %s ist Ihnen nicht erlaubt", "Sharing %s failed, because you can not share with yourself" : "Freigabe von %s fehlgeschlagen, da das Teilen mit sich selbst nicht möglich ist", "Sharing %s failed, because the user %s does not exist" : "Freigabe von %s fehlgeschlagen, da der Benutzer %s nicht existiert", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Freigabe von %s fehlgeschlagen, da der Benutzer %s kein Gruppenmitglied einer der Gruppen von %s ist", "Sharing %s failed, because this item is already shared with %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit %s geteilt wird", "Sharing %s failed, because this item is already shared with user %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit dem Benutzer %s geteilt wird", "Sharing %s failed, because the group %s does not exist" : "Freigabe von %s fehlgeschlagen, da die Gruppe %s nicht existiert", "Sharing %s failed, because %s is not a member of the group %s" : "Freigabe von %s fehlgeschlagen, da %s kein Mitglied der Gruppe %s ist", "You need to provide a password to create a public link, only protected links are allowed" : "Es sind nur geschützte Links zulässig, daher müssen Sie ein Passwort angeben, um einen öffentlichen Link zu generieren", "Sharing %s failed, because sharing with links is not allowed" : "Freigabe von %s fehlgeschlagen, da das Teilen von Verknüpfungen nicht erlaubt ist", "Not allowed to create a federated share with the same user" : "Das Erstellen einer Federated-Cloud-Freigabe mit dem gleichen Benutzer ist nicht erlaubt", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Freigabe von %s fehlgeschlagen, da %s nicht gefunden wurde. Möglicherweise ist der Server nicht erreichbar.", "Share type %s is not valid for %s" : "Freigabetyp %s ist nicht gültig für %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Ablaufdatum kann nicht gesetzt werden. Freigaben können nach dem Teilen, nicht länger als %s gültig sein.", "Cannot set expiration date. Expiration date is in the past" : "Ablaufdatum kann nicht gesetzt werden. Ablaufdatum liegt in der Vergangenheit.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Freigabe-Backend %s muss in der OCP\\Share_Backend - Schnittstelle implementiert werden", "Sharing backend %s not found" : "Freigabe-Backend %s nicht gefunden", "Sharing backend for %s not found" : "Freigabe-Backend für %s nicht gefunden", "Sharing failed, because the user %s is the original sharer" : "Freigabe fehlgeschlagen, da der Benutzer %s der ursprünglich Teilende ist", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Freigabe von %s fehlgeschlagen, da die Berechtigungen die erteilten Berechtigungen %s überschreiten", "Sharing %s failed, because resharing is not allowed" : "Freigabe von %s fehlgeschlagen, da das nochmalige Freigeben einer Freigabe nicht erlaubt ist", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Freigabe von %s fehlgeschlagen, da das Freigabe-Backend für %s nicht in dieser Quelle gefunden werden konnte", "Sharing %s failed, because the file could not be found in the file cache" : "Freigabe von %s fehlgeschlagen, da die Datei im Datei-Cache nicht gefunden werden konnte", "Can’t increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen", "Files can’t be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden", "Files can’t be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden", "Expiration date is in the past" : "Das Ablaufdatum liegt in der Vergangenheit.", "Can’t set expiration date more than %s days in the future" : "Das Ablaufdatum kann nicht mehr als %s Tage in die Zukunft liegen", "%s shared »%s« with you" : "%s hat „%s“ mit Ihnen geteilt", "%s shared »%s« with you." : "%s hat mit Ihnen »%s« geteilt.", "Click the button below to open it." : "Klicken Sie zum Öffnen auf die untere Schaltfläche.", "Open »%s«" : "»%s« öffnen", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Die angeforderte Freigabe existiert nicht mehr", "Could not find category \"%s\"" : "Die Kategorie \"%s“ konnte nicht gefunden werden", "Sunday" : "Sonntag", "Monday" : "Montag", "Tuesday" : "Dienstag", "Wednesday" : "Mittwoch", "Thursday" : "Donnerstag", "Friday" : "Freitag", "Saturday" : "Samstag", "Sun." : "Son.", "Mon." : "Mon.", "Tue." : "Die.", "Wed." : "Mit.", "Thu." : "Don.", "Fri." : "Fre.", "Sat." : "Sam.", "Su" : "So", "Mo" : "Mo", "Tu" : "Di", "We" : "Mi", "Th" : "Do", "Fr" : "Fr", "Sa" : "Sa", "January" : "Januar", "February" : "Februar", "March" : "März", "April" : "April", "May" : "Mai", "June" : "Juni", "July" : "Juli", "August" : "August", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "Dezember", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mär.", "Apr." : "Apr.", "May." : "Mai", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dez.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Folgende Zeichen sind im Benutzernamen erlaubt: „a-z“, „A-Z“, „0-9“ und „_.@-'“", "A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden", "Username contains whitespace at the beginning or at the end" : "Der Benutzername enthält Leerzeichen am Anfang oder am Ende", "Username must not consist of dots only" : "Der Benutzername darf nicht nur aus Punkten bestehen", "A valid password must be provided" : "Es muss ein gültiges Passwort eingegeben werden", "The username is already being used" : "Dieser Benutzername existiert bereits", "Could not create user" : "Benutzer konnte nicht erstellt werden", "User disabled" : "Nutzer deaktiviert", "Login canceled by app" : "Anmeldung durch die App abgebrochen", "No app name specified" : "Es wurde kein App-Name angegeben", "App '%s' could not be installed!" : "'%s' - App konnte nicht installiert werden!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Die App „%s“ kann nicht installiert werden, da die folgenden Abhängigkeiten nicht erfüllt sind: %s", "a safe home for all your data" : "ein sicherer Ort für all Ihre Daten", "File is currently busy, please try again later" : "Die Datei ist in Benutzung, bitte versuchen Sie es später noch einmal", "Can't read file" : "Datei kann nicht gelesen werden", "Application is not enabled" : "Die Anwendung ist nicht aktiviert", "Authentication error" : "Authentifizierungsfehler", "Token expired. Please reload page." : "Token abgelaufen. Bitte laden Sie die Seite neu.", "Unknown user" : "Unbekannter Benutzer", "No database drivers (sqlite, mysql, or postgresql) installed." : "Keine Datenbanktreiber (SQLite, MySQL oder PostgreSQL) installiert.", "Cannot write into \"config\" directory" : "Schreiben in das „config“-Verzeichnis ist nicht möglich", "Cannot write into \"apps\" directory" : "Schreiben in das „apps“-Verzeichnis ist nicht möglich", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das App-Verzeichnis eingeräumt wird. Siehe auch %s", "Cannot create \"data\" directory" : "Kann das \"Daten\"-Verzeichnis nicht erstellen", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Berechtigungen können zumeist korrigiert werden indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s. ", "Setting locale to %s failed" : "Das Setzen der Umgebungslokale auf %s ist fehlgeschlagen", "Please install one of these locales on your system and restart your webserver." : "Bitte installieren Sie eine dieser Sprachen auf Ihrem System und starten Sie den Webserver neu.", "Please ask your server administrator to install the module." : "Bitte kontaktieren Sie Ihren Server-Administrator und bitten Sie um die Installation des Moduls.", "PHP module %s not installed." : "PHP-Modul %s nicht installiert.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-Einstellung „%s“ ist nicht auf „%s“ gesetzt.", "Adjusting this setting in php.ini will make Nextcloud run again" : "Eine Änderung dieser Einstellung in der php.ini kann Ihre Nextcloud wieder lauffähig machen.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload ist nicht auf den erwarteten Wert „0“, sondern stattdessen auf „%s“ gesetzt", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bitte setzen Sie zum Beheben dieses Problems <code>mbstring.func_overload</code> in Ihrer php.ini auf <code>0</code>.", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 ist mindestestens erforderlich. Im Moment ist %s installiert.", "To fix this issue update your libxml2 version and restart your web server." : "Um den Fehler zu beheben, müssen Sie die libxml2 Version aktualisieren und den Webserver neustarten.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ist offenbar so konfiguriert, dass PHPDoc-Blöcke in der Anweisung entfernt werden. Dadurch sind mehrere Kern-Apps nicht erreichbar.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dies wird wahrscheinlich durch Zwischenspeicher/Beschleuniger wie etwa Zend OPcache oder eAccelerator verursacht.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-Module wurden installiert, werden aber als noch fehlend gelistet?", "Please ask your server administrator to restart the web server." : "Bitte kontaktieren Sie Ihren Server-Administrator und bitten Sie um den Neustart des Webservers.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 benötigt", "Please upgrade your database version" : "Bitte aktualisieren Sie Ihre Datenbankversion", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Bitte ändern Sie die Berechtigungen auf 0770, so dass das Verzeichnis nicht von anderen Benutzern angezeigt werden kann.", "Your data directory is readable by other users" : "Ihr Datenverzeichnis kann von anderen Benutzern gelesen werden", "Your data directory must be an absolute path" : "Ihr Datenverzeichnis muss einen eindeutigen Pfad haben", "Check the value of \"datadirectory\" in your configuration" : "Überprüfen Sie bitte die Angabe unter „datadirectory“ in Ihrer Konfiguration", "Your data directory is invalid" : "Dein Datenverzeichnis ist ungültig.", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Stellen Sie sicher, dass eine Datei \".ocdata\" im Wurzelverzeichnis des data-Verzeichnisses existiert.", "Could not obtain lock type %d on \"%s\"." : "Sperrtyp %d auf „%s“ konnte nicht ermittelt werden.", "Storage unauthorized. %s" : "Speicher nicht authorisiert. %s", "Storage incomplete configuration. %s" : "Speicher-Konfiguration unvollständig. %s", "Storage connection error. %s" : "Verbindungsfehler zum Speicherplatz. %s", "Storage is temporarily not available" : "Speicher ist vorübergehend nicht verfügbar", "Storage connection timeout. %s" : "Zeitüberschreitung der Verbindung zum Speicherplatz. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Dies kann normalerweise behoben werden, %sindem dem Webserver Schreibzugriff auf das Konfigurationsverzeichnis gegeben wird %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte aktivieren Sie es in Ihren App-Einstellungen oder kontaktieren Sie Ihren Administrator.", "Server settings" : "Servereinstellungen", "DB Error: \"%s\"" : "DB-Fehler: „%s“", "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", "You need to enter either an existing account or the administrator." : "Sie müssen entweder ein existierendes Benutzerkonto oder das Administratorenkonto angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Das Setzen der Berechtigungen für %s ist fehlgeschlagen, da die neuen Berechtigungen, die erteilten Berechtigungen %s überschreiten", "Setting permissions for %s failed, because the item was not found" : "Das Setzen der Berechtigungen für %s ist fehlgeschlagen, da das Element nicht gefunden wurde", "Cannot clear expiration date. Shares are required to have an expiration date." : "Ablaufdatum kann nicht gelöscht werden. Freigaben werden für ein Ablaufdatum benötigt.", "Cannot increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen", "Files can't be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden", "Files can't be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden", "Cannot set expiration date more than %s days in the future" : "Das Ablaufdatum kann nicht mehr als %s Tage in die Zukunft liegen", "Personal" : "Persönlich", "Admin" : "Verwaltung", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Dies kann normalerweise behoben werden, %sindem dem Webserver Schreibzugriff auf das App-Verzeichnis gegeben wird%s oder der App Store in der Konfigurationsdatei deaktiviert wird.", "Cannot create \"data\" directory (%s)" : "Erstellen des „data“-Verzeichnisses ist nicht möglich (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Dies kann normalerweise repariert werden, indem dem Webserver <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> Schreibzugriff auf das Wurzelverzeichnis gegeben wird</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Berechtigungen können normalerweise repariert werden, indem dem Webserver %s Schreibzugriff auf das Wurzelverzeichnis %s gegeben wird.", "Data directory (%s) is readable by other users" : "Datenverzeichnis (%s) ist von anderen Benutzern lesbar", "Data directory (%s) must be an absolute path" : "Das Datenverzeichnis (%s) muss ein absoluter Pfad sein", "Data directory (%s) is invalid" : "Datenverzeichnis (%s) ist ungültig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Bitte stellen Sie sicher, dass das Datenverzeichnis auf seiner ersten Ebene eine Datei namens „.ocdata“ enthält." }, "nplurals=2; plural=(n != 1);"); l10n/pl.js 0000604 00000056564 15247130447 0006312 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Nie można zapisać do katalogu \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Można to zwykle rozwiązać przez dodanie serwerowi www uprawnień zapisu do katalogu config.", "See %s" : "Zobacz %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Zwykle można to rozwiązać nadając serwerowi www uprawnienia do zapisu w katalogu konfiguracji. Zobacz %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Pliki aplikacji %1$s nie zostały zastąpione prawidłowo. Upewnij się, że to jest wersja kompatybilna z serwerem.", "Sample configuration detected" : "Wykryto przykładową konfigurację", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Wykryto skopiowanie przykładowej konfiguracji. To może popsuć Twoją instalację i nie jest wspierane. Proszę przeczytać dokumentację przed dokonywaniem zmian w config.php", "%1$s and %2$s" : "%1$s i %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s i %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s i %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s i %5$s", "Education Edition" : "Wersja edukacyjna", "Enterprise bundle" : "Zestaw biznesowy", "Groupware bundle" : "Zestaw pracy grupowej", "Social sharing bundle" : "Zestaw współdzielenia społecznościowego", "PHP %s or higher is required." : "PHP %s lub wyższe jest wymagane.", "PHP with a version lower than %s is required." : "Wersja PHP jest niższa niż %s, która jest wymagana.", "%sbit or higher PHP required." : "%sbit lub wyższe PHP jest wymagane.", "Following databases are supported: %s" : "Obsługiwane są następujące bazy danych: %s", "The command line tool %s could not be found" : "Narzędzie konsoli %s nie zostało znalezione", "The library %s is not available." : "Biblioteka %s nie jest dostępna.", "Library %s with a version higher than %s is required - available version %s." : "Biblioteka %s w wersji wyższej niż %s, która jest wymagana - dostępna wersja %s.", "Library %s with a version lower than %s is required - available version %s." : "Biblioteka w wersji %s jest niższa niż %s, która jest wymagana - dostępna wersja %s.", "Following platforms are supported: %s" : "Obsługiwane są następujące platformy: %s", "Server version %s or higher is required." : "Wersja serwera %s lub wyższa jest wymagana.", "Server version %s or lower is required." : "Wersja serwera %s lub niższa jest wymagana.", "Unknown filetype" : "Nieznany typ pliku", "Invalid image" : "Błędne zdjęcie", "Avatar image is not square" : "Obraz awataru nie jest kwadratowy", "today" : "dziś", "yesterday" : "wczoraj", "_%n day ago_::_%n days ago_" : ["%d dzień temu","%n dni temu","%n dni temu","%n dni temu"], "last month" : "w zeszłym miesiącu", "_%n month ago_::_%n months ago_" : ["%n miesiąc temu","%n miesięcy temu","%n miesięcy temu","%n miesięcy temu"], "last year" : "w zeszłym roku", "_%n year ago_::_%n years ago_" : ["%n rok temu","%n lata temu","%n lat temu","%n lat temu"], "_%n hour ago_::_%n hours ago_" : ["%n godzinę temu","%n godzin temu","%n godzin temu","%n godzin temu"], "_%n minute ago_::_%n minutes ago_" : ["%n minute temu","%n minut temu","%n minut temu","%n minut temu"], "seconds ago" : "sekund temu", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł o ID: %s nie istnieje. Proszę włącz go w ustawieniach aplikacji lub skontaktuj się z administratorem.", "File name is a reserved word" : "Nazwa pliku jest zarezerwowana", "File name contains at least one invalid character" : "Nazwa pliku zawiera co najmniej jeden nieprawidłowy znak", "File name is too long" : "Nazwa pliku zbyt długa", "Dot files are not allowed" : "Pliki z kropką są nie dozwolone", "Empty filename is not allowed" : "Pusta nazwa nie jest dozwolona.", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Aplikacja \"%s\" nie może zostać zainstalowana, ponieważ plik informacyjny nie może zostać odczytany.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikacja \"%s\" nie może zostać zainstalowana, ponieważ jest niekompatybilna z obecną wersją serwera.", "This is an automatically sent email, please do not reply." : "To jest automatycznie wysłany e-mail, proszę nie odpowiadać na niego.", "Help" : "Pomoc", "Apps" : "Aplikacje", "Settings" : "Ustawienia", "Log out" : "Wyloguj", "Users" : "Użytkownicy", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Ustawienia podstawowe", "Sharing" : "Udostępnianie", "Security" : "Bepieczeństwo", "Encryption" : "Szyfrowanie", "Additional settings" : "Ustawienia dodatkowe", "Tips & tricks" : "Porady i wskazówki", "Personal info" : "Informacje Osobiste", "Sync clients" : "Synchronizuj z klientami", "Unlimited" : "Nielimitowane", "__language_name__" : "__nazwa_języka__", "Verifying" : "Weryfikacja", "Verifying …" : "Weryfikacja...", "Verify" : "Zweryfikuj", "%s enter the database username and name." : "Podaj nazwę bazy danych i nazwę użytkownika %s", "%s enter the database username." : "Podaj nazwę użytkownika %s", "%s enter the database name." : "Podaj nazwę bazy danych %s", "%s you may not use dots in the database name" : "Nie możesz używać kropek w nazwie bazy danych %s", "Oracle connection could not be established" : "Nie można ustanowić połączenia z bazą Oracle", "Oracle username and/or password not valid" : "Oracle: Nazwa użytkownika i/lub hasło jest niepoprawne", "PostgreSQL username and/or password not valid" : "PostgreSQL: Nazwa użytkownika i/lub hasło jest niepoprawne", "You need to enter details of an existing account." : "Musisz wprowadzić szczegóły istniejącego konta.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie jest wspierany i %s nie będzie działać poprawnie na tej platformie. Używasz na własne ryzyko!", "For the best results, please consider using a GNU/Linux server instead." : "Aby uzyskać najlepsze rezultaty, rozważ w to miejsce użycie serwera GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Wydaje się, że ta %s instancja używa PHP 32-bitowego środowiska i opcja open_basedir została ustawiona w php.ini. Spowoduje to problemy z plikami większymi niż 4 GB i jest wysoce niezalecane.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Proszę usunąć ustawienie open_basedir ze swojego php.ini albo przestaw na PHP 64-bitowe.", "Set an admin username." : "Ustaw nazwę administratora.", "Set an admin password." : "Ustaw hasło administratora.", "Can't create or write into the data directory %s" : "Nie można tworzyć ani zapisywać w katalogu %s", "Invalid Federated Cloud ID" : "Nieprawidłowy ID Stowarzyszonej Chmury", "Sharing %s failed, because the backend does not allow shares from type %i" : "Współdzielenie %s nie udało się, ponieważ backend nie pozwala na współdzielenie takiego typu jak %i.", "Sharing %s failed, because the file does not exist" : "Wspóldzielenie %s nie powiodło się. ponieważ plik nie istnieje", "You are not allowed to share %s" : "Nie masz uprawnień aby udostępnić %s", "Sharing %s failed, because you can not share with yourself" : "Współdzielenie %s nie udało się, ponieważ nie możesz współdzielić sam ze sobą", "Sharing %s failed, because the user %s does not exist" : "Współdzielenie %s nie powiodło się, ponieważ użytkownik %s nie istnieje", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Współdzielenie %s nie powiodło się, ponieważ użytkownik %s nie jest członkiem żadnej grupy której członkiem jest %s", "Sharing %s failed, because this item is already shared with %s" : "Współdzielenie %s nie powiodło się, ponieważ element jest już współdzielony z %s", "Sharing %s failed, because this item is already shared with user %s" : "Współdzielenie %s nie udało się, ponieważ ten obiekt już jest współdzielony z użytkownikiem %s", "Sharing %s failed, because the group %s does not exist" : "Współdzielenie %s nie powiodło się, ponieważ grupa %s nie istnieje", "Sharing %s failed, because %s is not a member of the group %s" : "Współdzielenie %s nie powiodło się, ponieważ %s nie jest członkiem grupy %s", "You need to provide a password to create a public link, only protected links are allowed" : "Musisz zapewnić hasło aby utworzyć link publiczny, dozwolone są tylko linki chronione", "Sharing %s failed, because sharing with links is not allowed" : "Współdzielenie %s nie powiodło się, ponieważ współdzielenie z linkami nie jest dozwolone", "Not allowed to create a federated share with the same user" : "Nie jest dozwolone tworzenie współdzielenia stowarzyszonego z tym samym użytkownikiem", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Współdzielenie %s nie powiodło się, nie można odnaleźć %s. Prawdopobnie serwer nie jest teraz osiągalny.", "Share type %s is not valid for %s" : "Typ udziału %s nie jest właściwy dla %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Nie można ustawić daty wygaśnięcia. Udziały nie mogą wygasać później niż %s od momentu udostępnienia", "Cannot set expiration date. Expiration date is in the past" : "Nie można ustawić daty wygaśnięcia. Data wygaśnięcia jest w przeszłości.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Zaplecze do współdzielenia %s musi implementować interfejs OCP\\Share_Backend", "Sharing backend %s not found" : "Zaplecze %s do współdzielenia nie zostało znalezione", "Sharing backend for %s not found" : "Zaplecze do współdzielenia %s nie zostało znalezione", "Sharing failed, because the user %s is the original sharer" : "Współdzielenie z użytkownikiem %s się nie udało, ponieważ już jest współdzielenie z tym użytkownikiem.", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Współdzielenie %s nie powiodło się, ponieważ uprawnienia przekraczają te udzielone %s", "Sharing %s failed, because resharing is not allowed" : "Współdzielenie %s nie powiodło się, ponieważ ponowne współdzielenie nie jest dozwolone", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Współdzielenie %s nie powiodło się, ponieważ zaplecze współdzielenia dla %s nie mogło znaleźć jego źródła", "Sharing %s failed, because the file could not be found in the file cache" : "Współdzielenie %s nie powiodło się, ponieważ plik nie może zostać odnaleziony w buforze plików", "Can’t increase permissions of %s" : "Nie można zwiększyć praw dla 1%s", "Files can’t be shared with delete permissions" : "Pliki nie mogą zostać udostępnione z prawem do usuwania", "Files can’t be shared with create permissions" : "Pliki nie mogą zostać udostępnione z prawem do tworzenia", "Expiration date is in the past" : "Data ważności jest przeszła", "Can’t set expiration date more than %s days in the future" : "Nie można ustawić daty ważności dłuższej niż 1%s dni", "%s shared »%s« with you" : "%s współdzieli »%s« z tobą", "%s shared »%s« with you." : "%s współdzieli »%s« z Tobą.", "Click the button below to open it." : "Kliknij przycisk poniżej aby otworzyć.", "Open »%s«" : "Otwórz »%s«", "%s via %s" : "%s przez %s", "The requested share does not exist anymore" : "Żądany obiekt współdzielony już nie istnieje", "Could not find category \"%s\"" : "Nie można odnaleźć kategorii \"%s\"", "Sunday" : "Niedziela", "Monday" : "Poniedziałek", "Tuesday" : "Wtorek", "Wednesday" : "Środa", "Thursday" : "Czwartek", "Friday" : "Piątek", "Saturday" : "Sobota", "Sun." : "Nd.", "Mon." : "Pon.", "Tue." : "Wt.", "Wed." : "Śr.", "Thu." : "Czw.", "Fri." : "Pt.", "Sat." : "Sob.", "Su" : "Nd.", "Mo" : "Pon.", "Tu" : "Wt.", "We" : "Śr.", "Th" : "Czw.", "Fr" : "Pt.", "Sa" : "Sob.", "January" : "Styczeń", "February" : "Luty", "March" : "Marzec", "April" : "Kwiecień", "May" : "Maj", "June" : "Czerwiec", "July" : "Lipiec", "August" : "Sierpień", "September" : "Wrzesień", "October" : "Październik", "November" : "Listopad", "December" : "Grudzień", "Jan." : "Sty.", "Feb." : "Lut.", "Mar." : "Mar.", "Apr." : "Kwi.", "May." : "Maj.", "Jun." : "Cze.", "Jul." : "Lip.", "Aug." : "Sie.", "Sep." : "Wrz.", "Oct." : "Paź.", "Nov." : "Lis.", "Dec." : "Gru.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "W nazwie użytkownika dozwolone są tylko następujące znaki : \"a-z\", \"A-Z\", \"0-9\" i \"_.@-'\"", "A valid username must be provided" : "Należy podać prawidłową nazwę użytkownika", "Username contains whitespace at the beginning or at the end" : "Nazwa użytkownika zawiera spację na początku albo na końcu", "Username must not consist of dots only" : "Nazwa użytkownika nie może się składać tylko z kropek", "A valid password must be provided" : "Należy podać prawidłowe hasło", "The username is already being used" : "Ta nazwa użytkownika jest już używana", "Could not create user" : "Nie można utworzyć użytkownika.", "User disabled" : "Użytkownik zablokowany", "Login canceled by app" : "Zalogowanie anulowane przez aplikację", "No app name specified" : "Nie określono nazwy aplikacji", "App '%s' could not be installed!" : "Aplikacja '%s' nie mogła zostać zainstalowana!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Aplikacja \"%s\" nie może zostać zainstalowana, ponieważ następujące zależności nie zostały spełnione: %s", "a safe home for all your data" : "Bezpieczny dom dla twoich danych", "File is currently busy, please try again later" : "Plik jest obecnie niedostępny, proszę spróbować ponownie później", "Can't read file" : "Nie można odczytać pliku.", "Application is not enabled" : "Aplikacja nie jest włączona", "Authentication error" : "Błąd uwierzytelniania", "Token expired. Please reload page." : "Token wygasł. Proszę ponownie załadować stronę.", "Unknown user" : "Nieznany użytkownik", "No database drivers (sqlite, mysql, or postgresql) installed." : "Brak sterowników bazy danych (sqlite, mysql or postgresql).", "Cannot write into \"config\" directory" : "Nie można zapisać do katalogu \"config\"", "Cannot write into \"apps\" directory" : "Nie można zapisać do katalogu \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Zazwyczaj można to naprawić poprzez nadanie serwerowi www uprawnień do zapisu w katalogu aplikacji lub poprzez wyłączenie sklepu aplikacji w pliku konfiugracyjnym. Zobacz %s", "Cannot create \"data\" directory" : "Nie mozna utworzyć katalogu \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Zazwyczaj można to naprawić poprzez nadanie serwerowi www uprawnień do zapisu w katalogu głównym. Zobacz %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Uprawnienia mogą zazwyczaj być naprawione poprzez nadanie serwerowi www uprawnień do zapisu w katalogu głównym. Zobacz %s.", "Setting locale to %s failed" : "Nie udało się zmienić języka na %s", "Please install one of these locales on your system and restart your webserver." : "Proszę zainstalować jedno z poniższych locale w Twoim systemie i uruchomić ponownie serwer www.", "Please ask your server administrator to install the module." : "Proszę poproś administratora serwera aby zainstalował ten moduł.", "PHP module %s not installed." : "Moduł PHP %s nie jest zainstalowany.", "PHP setting \"%s\" is not set to \"%s\"." : "Ustawienie PHP \"%s\" nie jest ustawione na \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Modyfikacja tego w php.ini spowoduje, że Nextcloud ponownie będzie działał", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload jest ustawione na \"%s\" zamiast oczekiwanej wartości \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Aby naprawić ten problem ustaw <code>mbstring.func_overload</code> na <code>0</code> w swoim php.ini.", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Wymagana wersja libxml2 to przynajmniej 2.7.0. Obecnie jest zainstalowana wersja %s.", "To fix this issue update your libxml2 version and restart your web server." : "Aby naprawić ten problem zaktualizuj swoją wersję libxml2 i zrestartuj serwer web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Najwidoczniej PHP jest tak ustawione, aby wycinać bloki wklejonych dokumentów. Może to spowodować, że niektóre wbudowane aplikacje będą niedostępne.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dzieje się tak prawdopodobnie przez cache lub akcelerator taki jak Zend OPcache lub eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Moduły PHP zostały zainstalowane, ale nadal brakuje ich na liście?", "Please ask your server administrator to restart the web server." : "Poproś administratora serwera o restart serwera www.", "PostgreSQL >= 9 required" : "Wymagany PostgreSQL >= 9", "Please upgrade your database version" : "Uaktualnij wersję bazy danych", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Zmień uprawnienia na 0770, żeby ukryć zawartość katalogu przed innymi użytkownikami.", "Your data directory is readable by other users" : "Twój katalog z danymi mogą czytać inni użytkownicy", "Your data directory must be an absolute path" : "Twój katalog z danymi musi być ścieżką absolutną", "Check the value of \"datadirectory\" in your configuration" : "Sprawdź wartość \"datadirectory\" w swojej konfiguracji", "Your data directory is invalid" : "Twój katalog z danymi jest nieprawidłowy", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Upewnij się, że istnieje plik \".ocdata\" w katalogu z danymi, data/", "Could not obtain lock type %d on \"%s\"." : "Nie można uzyskać blokady typu %d na \"%s\".", "Storage unauthorized. %s" : "Magazyn nieautoryzowany. %s", "Storage incomplete configuration. %s" : "Niekompletna konfiguracja magazynu. %s", "Storage connection error. %s" : "Błąd połączenia z magazynem. %s", "Storage is temporarily not available" : "Magazyn jest tymczasowo niedostępny", "Storage connection timeout. %s" : "Limit czasu połączenia do magazynu został przekroczony. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Można to zwykle rozwiązać przez %sdodanie serwerowi www uprawnień zapisu do katalogu config%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł z id: %s nie istnieje. Należy go włączyć w ustawieniach aplikacji lub skontaktować się z administratorem.", "Server settings" : "Ustawienia serwera", "DB Error: \"%s\"" : "Błąd DB: \"%s\"", "Offending command was: \"%s\"" : "Niepoprawna komenda: \"%s\"", "You need to enter either an existing account or the administrator." : "Należy wprowadzić istniejące konto użytkownika lub administratora.", "Offending command was: \"%s\", name: %s, password: %s" : "Niepoprawne polecania: \"%s\", nazwa: %s, hasło: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Ustawienie uprawnień dla %s nie powiodło się, ponieważ uprawnienia wykraczają poza przydzielone %s", "Setting permissions for %s failed, because the item was not found" : "Ustawienie uprawnień dla %s nie powiodło się, ponieważ element nie został znaleziony", "Cannot clear expiration date. Shares are required to have an expiration date." : "Nie można wyczyścić daty wygaśnięcia. Współudziały muszą posiadać datę wygaśnięcia.", "Cannot increase permissions of %s" : "Nie można zwiększyć uprawnienia %s", "Files can't be shared with delete permissions" : "Pliki nie mogą być współdzielone z uprawnieniami kasowania", "Files can't be shared with create permissions" : "Pliki nie mogą być współdzielony z uprawnieniami tworzenia", "Cannot set expiration date more than %s days in the future" : "Nie można utworzyć daty wygaśnięcia na %s dni do przodu", "Personal" : "Osobiste", "Admin" : "Administracja", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Można to zwykle rozwiązać przez %sdodanie serwerowi www uprawnień zapisu do katalogu apps%s lub wyłączenie appstore w pliku konfiguracyjnym.", "Cannot create \"data\" directory (%s)" : "Nie można utworzyć katalogu \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Z reguły to może zostać naprawione <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">poprzez danie serwerowi web praw zapisu do katalogu domowego aplikacji</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Problemy z uprawnieniami można zwykle naprawić przez %sdodanie serwerowi www uprawnień zapisu do katalogu głównego%s.", "Data directory (%s) is readable by other users" : "Katalog danych (%s) jest możliwy do odczytania przez innych użytkowników", "Data directory (%s) must be an absolute path" : "Katalog danych (%s) musi być ścieżką absolutną", "Data directory (%s) is invalid" : "Katalog danych (%s) jest nieprawidłowy", "Please check that the data directory contains a file \".ocdata\" in its root." : "Sprawdź, czy katalog danych zawiera plik \".ocdata\"." }, "nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);"); l10n/nb.js 0000604 00000052474 15247130447 0006272 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Kan ikke skrive til «config»-mappen!", "This can usually be fixed by giving the webserver write access to the config directory" : "Dette kan vanligvis ordnes ved å gi vev-tjeneren skrivetilgang til config-mappen", "See %s" : "Se %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dette kan vanligvis ordnes ved å gi vev-tjeneren skrivetilgang til config-mappen. Se %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Filene i appen %$1s ble ikke erstattet skikkelig. Sjekk at versjonen er kompatibel med tjeneren.", "Sample configuration detected" : "Eksempeloppsett oppdaget", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Det ble oppdaget at eksempeloppsettet er blitt kopiert. Dette kan ødelegge installasjonen din og støttes ikke. Les dokumentasjonen før du gjør endringer i config.php", "%1$s and %2$s" : "%1$s og %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s og %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s og %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s og %5$s", "Education Edition" : "Utdanningsversjon", "Enterprise bundle" : "Bedrifts-pakke", "Groupware bundle" : "Gruppevare-pakke", "Social sharing bundle" : "Sosialdelings-pakke", "PHP %s or higher is required." : "PHP %s eller nyere kreves.", "PHP with a version lower than %s is required." : "PHP med en versjon lavere enn %s kreves.", "%sbit or higher PHP required." : "%sbit eller høyere PHP kreves", "Following databases are supported: %s" : "Følgende databaser støttes: %s", "The command line tool %s could not be found" : "Kommandolinjeverktøyet %s ble ikke funnet", "The library %s is not available." : "Biblioteket %s er ikke tilgjengelig.", "Library %s with a version higher than %s is required - available version %s." : "Bibliotek %s med en versjon høyere enn %s kreves - tilgjengelig versjon %s.", "Library %s with a version lower than %s is required - available version %s." : "Bibliotek %s med en versjon lavere nn %s kreves - tilgjengelig version %s.", "Following platforms are supported: %s" : "Følgende plattformer støttes: %s", "Server version %s or higher is required." : "Tjenerversjon %s eller høyere kreves.", "Server version %s or lower is required." : "Tjenerversjon %s eller lavere kreves.", "Unknown filetype" : "Ukjent filtype", "Invalid image" : "Ugyldig bilde", "Avatar image is not square" : "Avatarbilde er ikke firkantet", "today" : "i dag", "yesterday" : "i går", "_%n day ago_::_%n days ago_" : ["%n dag siden","%n dager siden"], "last month" : "forrige måned", "_%n month ago_::_%n months ago_" : ["for %n måned siden","for %n måneder siden"], "last year" : "forrige år", "_%n year ago_::_%n years ago_" : ["%n år siden","%n år siden"], "_%n hour ago_::_%n hours ago_" : ["for %n time siden","for %n timer siden"], "_%n minute ago_::_%n minutes ago_" : ["for %n minutt siden","for %n minutter siden"], "seconds ago" : "for få sekunder siden", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finnes ikke. Skru den på i programinnstillingene eller kontakt en administrator.", "File name is a reserved word" : "Filnavnet er et reservert ord", "File name contains at least one invalid character" : "Filnavnet inneholder minst ett ulovlig tegn", "File name is too long" : "Filnavnet er for langt", "Dot files are not allowed" : "Punktum-filer er ikke tillatt", "Empty filename is not allowed" : "Tomt filnavn er ikke tillatt", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Programmet \"%s\" kan ikke installeres på grunn av at appinfo-filen ikke kan leses.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Programmet \"%s\" kan ikke installere fordi det ikke er kompatibel med denne tjenerversjonen.", "This is an automatically sent email, please do not reply." : "Dette er en automatisk sendt e-post, ikke svar.", "Help" : "Hjelp", "Apps" : "Programmer", "Settings" : "Innstillinger", "Log out" : "Logg ut", "Users" : "Brukere", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Grunninnstillinger", "Sharing" : "Deling", "Security" : "Sikkerhet", "Encryption" : "Kryptering", "Additional settings" : "Flere innstillinger", "Tips & tricks" : "Tips og triks", "Personal info" : "Personlig informasjon", "Sync clients" : "Synkroniser klienter", "Unlimited" : "Ubegrenset", "__language_name__" : "Norsk bokmål", "Verifying" : "Bekrefter", "Verifying …" : "Bekrefter…", "Verify" : "Bekreft", "%s enter the database username and name." : "%s legg inn database brukernavn og navn.", "%s enter the database username." : "%s legg inn brukernavn for databasen.", "%s enter the database name." : "%s legg inn navnet på databasen.", "%s you may not use dots in the database name" : "%s du kan ikke bruke punktum i databasenavnet", "Oracle connection could not be established" : "Klarte ikke å etablere forbindelse til Oracle", "Oracle username and/or password not valid" : "Oracle-brukernavn og/eller passord er ikke gyldig", "PostgreSQL username and/or password not valid" : "PostgreSQL-brukernavn og/eller passord er ikke gyldig", "You need to enter details of an existing account." : "Du må legge in detaljene til en eksisterende konto.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X støttes ikke og %s vil ikke fungere korrekt på denne plattformen. Bruk på egen risiko!", "For the best results, please consider using a GNU/Linux server instead." : "For beste resultat, vurder å bruke en GNU/Linux-tjener i stedet.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Det ser ut for at %s-instansen kjører i et 32-bit PHP-miljø med open_basedir satt opp i php.ini. Dette vil føre til problemer med filer over 4 GB og frarådes på det sterkeste.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Fjern innstillingen open_basedir i php.ini eller bytt til 64-bit PHP.", "Set an admin username." : "Sett et admin-brukernavn.", "Set an admin password." : "Sett et admin-passord.", "Can't create or write into the data directory %s" : "Kan ikke opprette eller skrive i datamappen %s", "Invalid Federated Cloud ID" : "Ugyldig ID for sammenknyttet sky", "Sharing %s failed, because the backend does not allow shares from type %i" : "Deling av %s mislyktes, fordi tjeneren ikke tillater delinger fra type %i", "Sharing %s failed, because the file does not exist" : "Deling av %s mislyktes, fordi filen ikke eksisterer", "You are not allowed to share %s" : "Du har ikke lov til å dele %s", "Sharing %s failed, because you can not share with yourself" : "Deling av %s mislyktes fordi du ikke kan dele med deg selv", "Sharing %s failed, because the user %s does not exist" : "Deling av %s mislyktes, fordi brukeren %s ikke finnes", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Deling av %s mislyktes, fordi brukeren %s ikke er medlem av noen grupper som %s er medlem av", "Sharing %s failed, because this item is already shared with %s" : "Deling av %s mislyktes, fordi dette elementet allerede er delt med %s", "Sharing %s failed, because this item is already shared with user %s" : "Deling av %s mislyktes, fordi dette elementet allerede er delt med bruker %s", "Sharing %s failed, because the group %s does not exist" : "Deling av %s mislyktes, fordi gruppen %s ikke finnes", "Sharing %s failed, because %s is not a member of the group %s" : "Deling av %s mislyktes, fordi %s ikke er medlem av gruppen %s", "You need to provide a password to create a public link, only protected links are allowed" : "Du må oppgi et passord for å lage en offentlig lenke. Bare beskyttede lenker er tillatt", "Sharing %s failed, because sharing with links is not allowed" : "Deling av %s mislyktes, fordi deling med lenker ikke er tillatt", "Not allowed to create a federated share with the same user" : "Ikke tillatt å opprette en sammenknyttet sky-deling med den samme brukeren", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Deling %s mislyktes, fant ikke %s, kanskje tjeneren er utilgjengelig for øyeblikket.", "Share type %s is not valid for %s" : "Delingstype %s er ikke gyldig for %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Kan ikke sette utøpsdato. Delinger kan ikke utløpe senere enn %s etter at de har blitt delt", "Cannot set expiration date. Expiration date is in the past" : "Kan ikke sette utløpsdato. Utløpsdato er tilbake i tid", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Delings-tjener %s må implementere grensesnittet OCP\\Share_Backend", "Sharing backend %s not found" : "Delings-tjener %s ikke funnet", "Sharing backend for %s not found" : "Delings-tjener for %s ikke funnet", "Sharing failed, because the user %s is the original sharer" : "Deling mislyktes fordi brukeren %s er den som delte opprinnelig", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Deling av %s mislyktes, fordi tillatelsene går utover tillatelsene som er gitt til %s", "Sharing %s failed, because resharing is not allowed" : "Deling av %s mislyktes, fordi videre-deling ikke er tillatt", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Deling av %s mislyktes, fordi delings-bakenden for %s ikke kunne finne kilden", "Sharing %s failed, because the file could not be found in the file cache" : "Deling av %s mislyktes, fordi filen ikke ble funnet i fil-mellomlageret", "Can’t increase permissions of %s" : "Kan ikke øke tillatelser for %s", "Files can’t be shared with delete permissions" : "Filer kan ikke deles med tilgang til sletting", "Files can’t be shared with create permissions" : "Filer kan ikke deles med tilgang til opprettelse", "Expiration date is in the past" : "Utløpsdato er i fortid", "Can’t set expiration date more than %s days in the future" : "Kan ikke sette utløpsdato mer enn %s dager i fremtiden", "%s shared »%s« with you" : "%s delte »%s« med deg", "%s shared »%s« with you." : "%s delte \"%s\" med deg.", "Click the button below to open it." : "Klikk på knappen nedenfor for å åpne den.", "Open »%s«" : "Åpne »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Forespurt ressurs finnes ikke lenger", "Could not find category \"%s\"" : "Kunne ikke finne kategori \"%s\"", "Sunday" : "Søndag", "Monday" : "Mandag", "Tuesday" : "Tirsdag", "Wednesday" : "Onsdag", "Thursday" : "Torsdag", "Friday" : "Fredag", "Saturday" : "Lørdag", "Sun." : "Søn.", "Mon." : "Man.", "Tue." : "Tir.", "Wed." : "Ons.", "Thu." : "Tirs.", "Fri." : "Fre.", "Sat." : "Lør.", "Su" : "Sø", "Mo" : "Ma", "Tu" : "Ti", "We" : "On", "Th" : "To", "Fr" : "Fr", "Sa" : "Lø", "January" : "Januar", "February" : "Februar", "March" : "Mars", "April" : "April", "May" : "Mai", "June" : "Juni", "July" : "Juli", "August" : "August", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "Desember", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Mai.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Des.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Bare disse tegnene tillates i et brukernavn: \"a-z\", \"A-Z\", \"0-9\" og \"_.@-'\"", "A valid username must be provided" : "Oppgi et gyldig brukernavn", "Username contains whitespace at the beginning or at the end" : "Brukernavn inneholder blanke på begynnelsen eller slutten", "Username must not consist of dots only" : "Brukernavn kan ikke bare bestå av punktum", "A valid password must be provided" : "Oppgi et gyldig passord", "The username is already being used" : "Brukernavnet er allerede i bruk", "Could not create user" : "Kunne ikke opprette bruker", "User disabled" : "Brukeren er deaktivert", "Login canceled by app" : "Innlogging avbrutt av app", "No app name specified" : "Intet programnavn spesifisert", "App '%s' could not be installed!" : "Programmet '%s' kunne ikke installeres!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Programmet \"%s\" kan ikke installeres fordi følgende avhengigheter ikke er tilfredsstilt: %s", "a safe home for all your data" : "et sikkert hjem for alle dine data", "File is currently busy, please try again later" : "Filen er opptatt for øyeblikket, prøv igjen senere", "Can't read file" : "Kan ikke lese fil", "Application is not enabled" : "Programmet er ikke påslått", "Authentication error" : "Autentikasjonsfeil", "Token expired. Please reload page." : "Symbol utløpt. Last inn siden på nytt.", "Unknown user" : "Ukjent bruker", "No database drivers (sqlite, mysql, or postgresql) installed." : "Ingen databasedrivere (sqlite, mysql, or postgresql) installert.", "Cannot write into \"config\" directory" : "Kan ikke skrive i \"config\"-mappen", "Cannot write into \"apps\" directory" : "Kan ikke skrive i \"apps\"-mappen", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Dette kan vanligvis ordnes ved å gi vev-tjeneren skrivetilgang til apps-mappen eller ved å skru av programbutikken i config-fila. Se %s", "Cannot create \"data\" directory" : "Kan ikke opprette \"data\"-mappe", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dette kan vanligvis ordnes ved å gi vev-tjeneren skrivetilgang til root-mappen. Se %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Tillatelser kan vanligvis ordnes ved å gi vevtjeneren skrivetilgang til rotmappa. Se %s.", "Setting locale to %s failed" : "Setting av nasjonale innstillinger til %s mislyktes.", "Please install one of these locales on your system and restart your webserver." : "Installer en av disse nasjonale innstillingene på systemet ditt og start vevtjeneren på nytt.", "Please ask your server administrator to install the module." : "Be tjener-administratoren om å installere modulen.", "PHP module %s not installed." : "PHP-modul %s er ikke installert.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-innstilling \"%s\" er ikke satt til \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ved å endre denne innstillingen i php.ini gjør at Nextcloud vil kjøre igjen.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload er satt til \"%s\" i stedet for den forventede verdien \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Sett <code>mbstring.func_overload</code> til <code>0</code> in php.ini for å fikse dette problemet", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Krever minst libxml2 2.7.0. Per nå er %s installert.", "To fix this issue update your libxml2 version and restart your web server." : "For å fikse dette problemet, oppdater din libxml2 versjon og omstart vevtjeneren.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Det ser ut til at at PHP er satt opp til å fjerne innebygde doc-blokker. Dette gjør at flere av kjerneapplikasjonene blir utilgjengelige.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dette forårsakes sannsynligvis av en bufrer/akselerator, som f.eks. Zend OPcache eller eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-moduler har blitt installert, men de listes fortsatt som fraværende?", "Please ask your server administrator to restart the web server." : "Be tjener-administratoren om å starte vevtjeneren på nytt.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 kreves", "Please upgrade your database version" : "Oppgrader databaseversjonen din", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Endre tillatelsene til 0770 slik at mappen ikke kan listes av andre brukere.", "Your data directory is readable by other users" : "Din datamappe kan leses av andre brukere", "Your data directory must be an absolute path" : "Din datamappe må være en absolutt sti", "Check the value of \"datadirectory\" in your configuration" : "Sjekk verdien for \"datadirectory\" i oppsettet ditt", "Your data directory is invalid" : "Din datamappe er ugyldig", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Forsikre deg om at det finnes ei fil kalt \".ocdata\" på rota av datamappa.", "Could not obtain lock type %d on \"%s\"." : "Klarte ikke å låse med type %d på \"%s\".", "Storage unauthorized. %s" : "Lager uautorisert: %s", "Storage incomplete configuration. %s" : "Ikke komplett oppsett for lager. %s", "Storage connection error. %s" : "Tilkoblingsfeil for lager. %s", "Storage is temporarily not available" : "Lagring er midlertidig utilgjengelig", "Storage connection timeout. %s" : "Tidsavbrudd ved tilkobling av lager: %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Dette kan vanligvis ordnes ved %så gi vev-tjeneren skrivetilgang til oppsettsmappen%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finnes ikke. Skru den på i programinnstillingene eller kontakt en administrator.", "Server settings" : "Tjenerinnstillinger", "DB Error: \"%s\"" : "Databasefeil: \"%s\"", "Offending command was: \"%s\"" : "Kommandoen som mislyktes: \"%s\"", "You need to enter either an existing account or the administrator." : "Du må legge inn enten en eksisterende konto eller administratoren.", "Offending command was: \"%s\", name: %s, password: %s" : "Kommandoen som mislyktes var: \"%s\", navn: %s, passord: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Setting av tillatelser for %s mislyktes, fordi tillatelsene gikk ut over tillatelsene som er gitt til %s", "Setting permissions for %s failed, because the item was not found" : "Setting av tillatelser for %s mislyktes, fordi elementet ikke ble funnet", "Cannot clear expiration date. Shares are required to have an expiration date." : "Kan ikke fjerne utløpsdato. Delinger må ha en utløpsdato.", "Cannot increase permissions of %s" : "Kan ikke øke tillatelser for %s", "Files can't be shared with delete permissions" : "Filer kan ikke deles med rettigheter til sletting", "Files can't be shared with create permissions" : "Filer kan ikke deles med rettigheter til å opprette", "Cannot set expiration date more than %s days in the future" : "Kan ikke sette utløpsdato mer enn %s dager fram i tid", "Personal" : "Personlig", "Admin" : "Admin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Dette kan vanligvis ordnes ved %så gi vev-tjeneren skrivetilgang til program-mappen%s eller ved å deaktivere programbutikken i config-filen.", "Cannot create \"data\" directory (%s)" : "Kan ikke opprette \"data\"-mappen (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Dette fikses vanligvis ved å <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">gi vevtjeneren skrivetilgang til rotmappen</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Tillatelser kan vanligvis ordnes ved %så gi vevtjeneren skrivetilgang til rotmappen%s.", "Data directory (%s) is readable by other users" : "Data-mappen (%s) kan leses av andre brukere", "Data directory (%s) must be an absolute path" : "Datamappen (%s) må være en absolutt sti", "Data directory (%s) is invalid" : "Data-mappe (%s) er ugyldig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Sjekk at det ligger en fil \".ocdata\" i roten av data-mappen." }, "nplurals=2; plural=(n != 1);"); l10n/it.js 0000604 00000055577 15247130447 0006316 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Impossibile scrivere nella cartella \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella \"config\"", "See %s" : "Vedi %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella di configurazione. Vedi %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "I file dell'applicazione %1$s non sono stati sostituiti correttamente. Assicurati che sia una versione compatibile con il server.", "Sample configuration detected" : "Configurazione di esempio rilevata", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "È stato rilevato che la configurazione di esempio è stata copiata. Ciò può compromettere la tua installazione e non è supportato. Leggi la documentazione prima di modificare il file config.php", "%1$s and %2$s" : "%1$s e %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s e %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s e %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s e %5$s", "Education Edition" : "Edizione didattica", "Enterprise bundle" : "Pacchetto Enterprise", "Groupware bundle" : "Pacchetto Groupware", "Social sharing bundle" : "Pacchetto Social sharing", "PHP %s or higher is required." : "Richiesto PHP %s o superiore", "PHP with a version lower than %s is required." : "Richiesta una versione di PHP minore di %s.", "%sbit or higher PHP required." : "Richiesto PHP %sbit o superiore.", "Following databases are supported: %s" : "I seguenti database sono supportati: %s", "The command line tool %s could not be found" : "Lo strumento da riga di comando %s non è stato trovato", "The library %s is not available." : "La libreria %s non è disponibile.", "Library %s with a version higher than %s is required - available version %s." : "Richiesta una versione della libreria %s maggiore di %s - versione disponibile %s.", "Library %s with a version lower than %s is required - available version %s." : "Richiesta una versione della libreria %s minore di %s - versione disponibile %s.", "Following platforms are supported: %s" : "Sono supportate le seguenti piattaforme: %s", "Server version %s or higher is required." : "È richiesta la versione %s o successiva.", "Server version %s or lower is required." : "È richiesta la versione %s o precedente.", "Unknown filetype" : "Tipo di file sconosciuto", "Invalid image" : "Immagine non valida", "Avatar image is not square" : "L'immagine personale non è quadrata", "today" : "oggi", "yesterday" : "ieri", "_%n day ago_::_%n days ago_" : ["%d giorno fa","%n giorni fa"], "last month" : "mese scorso", "_%n month ago_::_%n months ago_" : ["%n mese fa","%n mesi fa"], "last year" : "anno scorso", "_%n year ago_::_%n years ago_" : ["%n anno fa","%n anni fa"], "_%n hour ago_::_%n hours ago_" : ["%n ora fa","%n ore fa"], "_%n minute ago_::_%n minutes ago_" : ["%n minuto fa","%n minuti fa"], "seconds ago" : "secondi fa", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Il modulo con ID: %s non esiste. Abilitalo nelle impostazioni delle applicazioni o contatta il tuo amministratore.", "File name is a reserved word" : "Il nome del file è una parola riservata", "File name contains at least one invalid character" : "Il nome del file contiene almeno un carattere non valido", "File name is too long" : "Il nome del file è troppo lungo", "Dot files are not allowed" : "I file con un punto iniziale non sono consentiti", "Empty filename is not allowed" : "Un nome di file vuoto non è consentito", "App \"%s\" cannot be installed because appinfo file cannot be read." : "L'applicazione \"%s\" non può essere installata poiché il file appinfo non può essere letto.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "L'applicazione \"%s\" non può essere installata perché non è compatibile con questa versione del server.", "This is an automatically sent email, please do not reply." : "Questo è un messaggio di posta inviato automaticamente, non rispondere.", "Help" : "Aiuto", "Apps" : "Applicazioni", "Settings" : "Impostazioni", "Log out" : "Esci", "Users" : "Utenti", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Impostazioni di base", "Sharing" : "Condivisione", "Security" : "Sicurezza", "Encryption" : "Cifratura", "Additional settings" : "Impostazioni aggiuntive", "Tips & tricks" : "Suggerimenti e trucchi", "Personal info" : "Informazioni personali", "Sync clients" : "Client di sincronizzazione", "Unlimited" : "Illimitato", "__language_name__" : "Italiano", "Verifying" : "Verifica", "Verifying …" : "Verifica in corso...", "Verify" : "Verifica", "%s enter the database username and name." : "%s digita il nome utente e il nome del database.", "%s enter the database username." : "%s digita il nome utente del database.", "%s enter the database name." : "%s digita il nome del database.", "%s you may not use dots in the database name" : "%s non dovresti utilizzare punti nel nome del database", "Oracle connection could not be established" : "La connessione a Oracle non può essere stabilita", "Oracle username and/or password not valid" : "Nome utente e/o password di Oracle non validi", "PostgreSQL username and/or password not valid" : "Nome utente e/o password di PostgreSQL non validi", "You need to enter details of an existing account." : "Devi inserire i dettagli di un account esistente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X non è supportato e %s non funzionerà correttamente su questa piattaforma. Usalo a tuo rischio!", "For the best results, please consider using a GNU/Linux server instead." : "Per avere il risultato migliore, prendi in considerazione l'utilizzo di un server GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Sembra che questa istanza di %s sia in esecuzione in un ambiente PHP a 32 bit e che open_basedir sia stata configurata in php.ini. Ciò comporterà problemi con i file più grandi di 4 GB ed è altamente sconsigliato.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Rimuovi l'impostazione di open_basedir nel tuo php.ini o passa alla versione a 64 bit di PHP.", "Set an admin username." : "Imposta un nome utente di amministrazione.", "Set an admin password." : "Imposta una password di amministrazione.", "Can't create or write into the data directory %s" : "Impossibile creare o scrivere nella cartella dei dati %s", "Invalid Federated Cloud ID" : "ID di cloud federata non valido", "Sharing %s failed, because the backend does not allow shares from type %i" : "Condivisione di %s non riuscita, poiché il motore non consente condivisioni del tipo %i", "Sharing %s failed, because the file does not exist" : "Condivisione di %s non riuscita, poiché il file non esiste", "You are not allowed to share %s" : "Non ti è consentito condividere %s", "Sharing %s failed, because you can not share with yourself" : "Condivisione di %s non riuscita, poiché non puoi condividere con te stesso", "Sharing %s failed, because the user %s does not exist" : "Condivisione di %s non riuscita, poiché l'utente %s non esiste", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Condivisione di %s non riuscita, poiché l'utente %s non appartiene ad alcun gruppo di cui %s è membro", "Sharing %s failed, because this item is already shared with %s" : "Condivisione di %s non riuscita, poiché l'oggetto è già condiviso con %s", "Sharing %s failed, because this item is already shared with user %s" : "Condivisione di %s non riuscita, poiché l'oggetto è già condiviso con l'utente %s", "Sharing %s failed, because the group %s does not exist" : "Condivisione di %s non riuscita, poiché il gruppo %s non esiste", "Sharing %s failed, because %s is not a member of the group %s" : "Condivisione di %s non riuscita, poiché %s non appartiene al gruppo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Devi fornire una password per creare un collegamento pubblico, sono consentiti solo i collegamenti protetti", "Sharing %s failed, because sharing with links is not allowed" : "Condivisione di %s non riuscita, poiché i collegamenti non sono consentiti", "Not allowed to create a federated share with the same user" : "Non è consentito creare una condivisione federata con lo stesso utente", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "La condivisione di %s non è riuscita, impossibile trovare %s, è probabile che il server non sia al momento raggiungibile.", "Share type %s is not valid for %s" : "Il tipo di condivisione %s non è valido per %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Impossibile impostare la data di scadenza. Le condivisioni non possono scadere più tardi di %s dalla loro attivazione", "Cannot set expiration date. Expiration date is in the past" : "Impossibile impostare la data di scadenza. La data di scadenza è nel passato.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Il motore di condivisione %s deve implementare l'interfaccia OCP\\Share_Backend", "Sharing backend %s not found" : "Motore di condivisione %s non trovato", "Sharing backend for %s not found" : "Motore di condivisione di %s non trovato", "Sharing failed, because the user %s is the original sharer" : "Condivisione non riuscita, poiché l'utente %s ha condiviso in origine", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Condivisione di %s non riuscita, poiché i permessi superano quelli accordati a %s", "Sharing %s failed, because resharing is not allowed" : "Condivisione di %s non riuscita, poiché la ri-condivisione non è consentita", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Condivisione di %s non riuscita, poiché il motore di condivisione per %s non riesce a trovare la sua fonte", "Sharing %s failed, because the file could not be found in the file cache" : "Condivisione di %s non riuscita, poiché il file non è stato trovato nella cache", "Can’t increase permissions of %s" : "Impossibile aumentare i permessi di %s", "Files can’t be shared with delete permissions" : "I file non possono essere condivisi con permessi di eliminazione", "Files can’t be shared with create permissions" : "I file non possono essere condivisi con permessi di creazione", "Expiration date is in the past" : "La data di scadenza è nel passato", "Can’t set expiration date more than %s days in the future" : "Impossibile impostare la data di scadenza a più di %s giorni nel futuro", "%s shared »%s« with you" : "%s ha condiviso «%s» con te", "%s shared »%s« with you." : "%s ha condiviso «%s» con te.", "Click the button below to open it." : "Fai clic sul pulsante sotto per aprirlo.", "Open »%s«" : "Apri «%s»", "%s via %s" : "%s tramite %s", "The requested share does not exist anymore" : "La condivisione richiesta non esiste più", "Could not find category \"%s\"" : "Impossibile trovare la categoria \"%s\"", "Sunday" : "Domenica", "Monday" : "Lunedì", "Tuesday" : "Martedì", "Wednesday" : "Mercoledì", "Thursday" : "Giovedì", "Friday" : "Venerdì", "Saturday" : "Sabato", "Sun." : "Dom.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mer.", "Thu." : "Gio.", "Fri." : "Ven.", "Sat." : "Sab.", "Su" : "Do", "Mo" : "Lu", "Tu" : "Ma", "We" : "Me", "Th" : "Gi", "Fr" : "Ve", "Sa" : "Sa", "January" : "Gennaio", "February" : "Febbraio", "March" : "Marzo", "April" : "Aprile", "May" : "Maggio", "June" : "Giugno", "July" : "Luglio", "August" : "Agosto", "September" : "Settembre", "October" : "Ottobre", "November" : "Novembre", "December" : "Dicembre", "Jan." : "Gen.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Mag.", "Jun." : "Giu.", "Jul." : "Lug.", "Aug." : "Ago.", "Sep." : "Set.", "Oct." : "Ott.", "Nov." : "Nov.", "Dec." : "Dic.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Solo i seguenti caratteri sono consentiti in un nome utente: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-'\"", "A valid username must be provided" : "Deve essere fornito un nome utente valido", "Username contains whitespace at the beginning or at the end" : "Il nome utente contiene spazi all'inizio o alla fine", "Username must not consist of dots only" : "Il nome utente non può consistere di soli punti", "A valid password must be provided" : "Deve essere fornita una password valida", "The username is already being used" : "Il nome utente è già utilizzato", "Could not create user" : "Impossibile creare l'utente", "User disabled" : "Utente disabilitato", "Login canceled by app" : "Accesso annullato dall'applicazione", "No app name specified" : "Il nome dell'applicazione non è specificato", "App '%s' could not be installed!" : "L'applicazione '%s' non può essere installata!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "L'applicazione \"%s\" non può essere installata poiché le seguenti dipendenze non sono soddisfatte: %s", "a safe home for all your data" : "un posto sicuro per tutti i tuoi dati", "File is currently busy, please try again later" : "Il file è attualmente occupato, riprova più tardi", "Can't read file" : "Impossibile leggere il file", "Application is not enabled" : "L'applicazione non è abilitata", "Authentication error" : "Errore di autenticazione", "Token expired. Please reload page." : "Token scaduto. Ricarica la pagina.", "Unknown user" : "Utente sconosciuto", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nessun driver di database (sqlite, mysql o postgresql) installato", "Cannot write into \"config\" directory" : "Impossibile scrivere nella cartella \"config\"", "Cannot write into \"apps\" directory" : "Impossibile scrivere nella cartella \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella delle applicazioni o disabilitando il negozio di applicazioni nel file di configurazione. Vedi %s", "Cannot create \"data\" directory" : "Impossibile creare la cartella \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella radice. Vedi %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "I permessi possono essere normalmente corretti fornendo al server web accesso in scrittura alla cartella radice. Vedi %s.", "Setting locale to %s failed" : "L'impostazione della localizzazione a %s non è riuscita", "Please install one of these locales on your system and restart your webserver." : "Installa una delle seguenti localizzazioni sul tuo sistema e riavvia il server web.", "Please ask your server administrator to install the module." : "Chiedi all'amministratore del tuo server di installare il modulo.", "PHP module %s not installed." : "Il modulo PHP %s non è installato.", "PHP setting \"%s\" is not set to \"%s\"." : "L'impostazione \"%s\" di PHP non è configurata a \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Per eseguire nuovamente Nextcloud, modificare questa impostazione nel file php.ini", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload è impostata a \"%s\" invece del valore atteso \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Per correggere questo problema, imposta <code>mbstring.func_overload</code> a <code>0</code> nel tuo php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "È richiesta almeno la versione 2.7.0 di libxml2. Quella attualmente installata è la %s.", "To fix this issue update your libxml2 version and restart your web server." : "Per risolvere questo problema, aggiorna la tua versione di libxml2 e riavvia il server web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Sembra che PHP sia configurato per rimuovere i blocchi di documentazione in linea. Ciò renderà inaccessibili diverse applicazioni principali.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Ciò è causato probabilmente da una cache/acceleratore come Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Sono stati installati moduli PHP, ma sono elencati ancora come mancanti?", "Please ask your server administrator to restart the web server." : "Chiedi all'amministratore di riavviare il server web.", "PostgreSQL >= 9 required" : "Richiesto PostgreSQL >= 9", "Please upgrade your database version" : "Aggiorna la versione del tuo database", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Modifica i permessi in 0770 in modo tale che la cartella non sia leggibile dagli altri utenti.", "Your data directory is readable by other users" : "La cartella dei dati è leggibile dagli altri utenti", "Your data directory must be an absolute path" : "La cartella dei dati deve essere un percorso assoluto", "Check the value of \"datadirectory\" in your configuration" : "Controlla il valore di \"datadirectory\" nella tua configurazione", "Your data directory is invalid" : "La cartella dei dati non è valida", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Assicurati che ci sia un file \".ocdata\" nella radice della cartella data.", "Could not obtain lock type %d on \"%s\"." : "Impossibile ottenere il blocco di tipo %d su \"%s\".", "Storage unauthorized. %s" : "Archiviazione non autorizzata. %s", "Storage incomplete configuration. %s" : "Configurazione dell'archiviazione incompleta.%s", "Storage connection error. %s" : "Errore di connessione all'archiviazione. %s", "Storage is temporarily not available" : "L'archiviazione è temporaneamente non disponibile", "Storage connection timeout. %s" : "Timeout di connessione all'archiviazione. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Ciò può essere normalmente corretto %sfornendo al server web accesso in scrittura alla cartella \"config\"%s", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Il modulo con id: %s non esiste. Abilitalo nelle impostazioni delle applicazioni o contatta il tuo amministratore.", "Server settings" : "Impostazioni server", "DB Error: \"%s\"" : "Errore DB: \"%s\"", "Offending command was: \"%s\"" : "Il comando non consentito era: \"%s\"", "You need to enter either an existing account or the administrator." : "È necessario inserire un account esistente o l'amministratore.", "Offending command was: \"%s\", name: %s, password: %s" : "Il comando non consentito era: \"%s\", nome: %s, password: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Impostazione permessi per %s non riuscita, poiché i permessi superano i permessi accordati a %s", "Setting permissions for %s failed, because the item was not found" : "Impostazione permessi per %s non riuscita, poiché l'elemento non è stato trovato", "Cannot clear expiration date. Shares are required to have an expiration date." : "Impossibile cancellare la data di scadenza. Le condivisioni devono avere una data di scadenza.", "Cannot increase permissions of %s" : "Impossibile aumentare i permessi di %s", "Files can't be shared with delete permissions" : "I file non possono essere condivisi con permessi di eliminazione", "Files can't be shared with create permissions" : "I file non possono essere condivisi con permessi di creazione", "Cannot set expiration date more than %s days in the future" : "Impossibile impostare la data di scadenza a più di %s giorni nel futuro", "Personal" : "Personale", "Admin" : "Admin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Ciò può essere normalmente corretto %sfornendo al server web accesso in scrittura alla cartella \"apps\"%s o disabilitando il negozio di applicazioni nel file di configurazione.", "Cannot create \"data\" directory (%s)" : "Impossibile creare la cartella \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Ciò può essere normalmente corretto <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">fornendo al server web accesso in scrittura alla cartella radice</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "I permessi possono essere normalmente corretti %sfornendo al server web accesso in scrittura alla cartella radice%s.", "Data directory (%s) is readable by other users" : "La cartella dei dati (%s) è leggibile dagli altri utenti", "Data directory (%s) must be an absolute path" : "La cartella dei dati (%s) deve essere un percorso assoluto", "Data directory (%s) is invalid" : "La cartella dei dati (%s) non è valida", "Please check that the data directory contains a file \".ocdata\" in its root." : "Verifica che la cartella dei dati contenga un file \".ocdata\" nella sua radice." }, "nplurals=2; plural=(n != 1);"); l10n/ru.js 0000604 00000073601 15247130447 0006314 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Запись в каталог «config» невозможна!", "This can usually be fixed by giving the webserver write access to the config directory" : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог конфигурации", "See %s" : "Смотрите %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог конфигурации. Смотрите %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Файлы приложения %$1s не заменены корректно. Проверьте что его версия совместима с версией сервера.", "Sample configuration detected" : "Обнаружена конфигурация из примера", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Была обнаружена конфигурация из примера. Такая конфигурация не поддерживается и может повредить вашей системе. Прочтите документацию перед внесением изменений в файл config.php", "%1$s and %2$s" : "%1$s и %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s и %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s и %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s и %5$s", "Education Edition" : "Образовательная редакция", "Enterprise bundle" : "Корпоративный пакет", "Groupware bundle" : "Пакет для групп", "Social sharing bundle" : "Пакет для соц. сетей", "PHP %s or higher is required." : "Требуется PHP %s или выше", "PHP with a version lower than %s is required." : "Требуется версия PHP ниже %s.", "%sbit or higher PHP required." : "Требуется PHP с разрядностью %s бит или более.", "Following databases are supported: %s" : "Поддерживаются следующие СУБД: %s", "The command line tool %s could not be found" : "Утилита командной строки %s не найдена", "The library %s is not available." : "Библиотека %s недоступна.", "Library %s with a version higher than %s is required - available version %s." : "Требуется библиотека %s версии не меньше %s, установлена версия %s.", "Library %s with a version lower than %s is required - available version %s." : "Требуется библиотека %s версии не выше %s, установлена версия %s.", "Following platforms are supported: %s" : "Поддерживаются следующие платформы: %s", "Server version %s or higher is required." : "Требуется сервер версии %s или выше.", "Server version %s or lower is required." : "Требуется сервер версии %s или ниже.", "Unknown filetype" : "Неизвестный тип файла", "Invalid image" : "Изображение повреждено", "Avatar image is not square" : "Изображение аватара не квадратное", "today" : "сегодня", "yesterday" : "вчера", "_%n day ago_::_%n days ago_" : ["%n день назад","%n дня назад","%n дней назад","%n дней назад"], "last month" : "в прошлом месяце", "_%n month ago_::_%n months ago_" : ["%n месяц назад","%n месяца назад","%n месяцев назад","%n месяцев назад"], "last year" : "в прошлом году", "_%n year ago_::_%n years ago_" : ["%n год назад","%n года назад","%n лет назад","%n лет назад"], "_%n hour ago_::_%n hours ago_" : ["%n час назад","%n часа назад","%n часов назад","%n часов назад"], "_%n minute ago_::_%n minutes ago_" : ["%n минута назад","%n минуты назад","%n минут назад","%n минут назад"], "seconds ago" : "менее минуты", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модуль с ID «%s» не существует. Включите его в настройках приложений или обратитесь к администратору.", "File name is a reserved word" : "Имя файла является зарезервированным словом", "File name contains at least one invalid character" : "Имя файла содержит по крайней мере один некорректный символ", "File name is too long" : "Имя файла слишком длинное.", "Dot files are not allowed" : "Файлы начинающиеся с точки не допускаются", "Empty filename is not allowed" : "Пустое имя файла не допускается", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Приложение «%s» не может быть установлено, так как файл с информацией о приложении не может быть прочтен.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Приложение «%s» не может быть установлено, потому что оно несовместимо с этой версией сервера", "This is an automatically sent email, please do not reply." : "Это соощение отправлено автоматически, пожалуйста, не отвечайте на него.", "Help" : "Помощь", "Apps" : "Приложения", "Settings" : "Настройки", "Log out" : "Выйти", "Users" : "Пользователи", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Основные настройки", "Sharing" : "Общий доступ", "Security" : "Безопасность", "Encryption" : "Шифрование", "Additional settings" : "Дополнительные настройки", "Tips & tricks" : "Советы и трюки", "Personal info" : "Личная информация", "Sync clients" : "Клиенты синхронизации", "Unlimited" : "Неограничено", "__language_name__" : "Русский", "Verifying" : "Производится проверка", "Verifying …" : "Производится проверка…", "Verify" : "Проверить", "%s enter the database username and name." : "%s укажите имя пользователя и название для базы данных.", "%s enter the database username." : "%s введите имя пользователя базы данных.", "%s enter the database name." : "%s введите имя базы данных.", "%s you may not use dots in the database name" : "%s Вы не можете использовать точки в имени базы данных", "Oracle connection could not be established" : "Соединение с Oracle не может быть установлено", "Oracle username and/or password not valid" : "Неверное имя пользователя и/или пароль Oracle", "PostgreSQL username and/or password not valid" : "Неверное имя пользователя и/или пароль PostgreSQL", "You need to enter details of an existing account." : "Необходимо уточнить данные существующего акаунта.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X не поддерживается и %s может работать некорректно на данной платформе. Используйте на свой страх и риск!", "For the best results, please consider using a GNU/Linux server instead." : "Для достижения наилучших результатов, рассмотрите вариант использования сервера на GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Кажется что экземпляр этого %s работает в 32-битной среде PHP и в php.ini был настроен open_basedir. Это приведёт к проблемам с файлами более 4 ГБ и настоятельно не рекомендуется.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Пожалуйста, удалите директиву open_basedir из файла php.ini или смените PHP на 64-разрядную сборку.", "Set an admin username." : "Задать имя пользователя для администратора.", "Set an admin password." : "Задать пароль для admin.", "Can't create or write into the data directory %s" : "Невозможно создать или записать в каталог данных %s", "Invalid Federated Cloud ID" : "Неверный ID в объединении облачных хранилищ.", "Sharing %s failed, because the backend does not allow shares from type %i" : "Не удалось поделиться %s, так как механизм хранения не допускает публикации из элементов типа %i", "Sharing %s failed, because the file does not exist" : "Не удалось поделиться %s, файл не существует", "You are not allowed to share %s" : "Вам не разрешено делиться %s", "Sharing %s failed, because you can not share with yourself" : "Не удалось поделиться %s. Вы не можете поделиться с самим собой.", "Sharing %s failed, because the user %s does not exist" : "Не удалось поделиться %s, пользователь %s не существует.", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Не удалось поделиться %s, так как пользователь %s не состоит в какой-либо группе, в которой состоит %s", "Sharing %s failed, because this item is already shared with %s" : "Не удалось поделиться %s, пользователь %s уже имеет доступ к этому элементу", "Sharing %s failed, because this item is already shared with user %s" : "Не удалось поделиться %s, так как элемент находится в общем доступе у %s", "Sharing %s failed, because the group %s does not exist" : "Не удалось поделиться %s, группа %s не существует", "Sharing %s failed, because %s is not a member of the group %s" : "Не удалось поделиться %s, пользователь %s не является членом группы %s", "You need to provide a password to create a public link, only protected links are allowed" : "Вам нужно задать пароль для создания публичной ссылки. Разрешены только защищённые ссылки", "Sharing %s failed, because sharing with links is not allowed" : "Не удалось поделиться %s, открытие доступа по ссылке запрещено", "Not allowed to create a federated share with the same user" : "Не допускается создание федеративного общего ресурса с тем же пользователем", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Не удалось поделиться %s, не удалось найти %s, возможно, сервер не доступен.", "Share type %s is not valid for %s" : "Тип общего доступа %s недопустим для %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Невозможно установить дату истечения. Общие ресурсы не могут устареть позже %s с момента их публикации.", "Cannot set expiration date. Expiration date is in the past" : "Невозможно установить дату окончания. Дата окончания в прошлом.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Бэкенд общего доступа %s должен реализовывать интерфейс OCP\\Share_Backend", "Sharing backend %s not found" : "Бэкенд общего доступа %s не найден", "Sharing backend for %s not found" : "Бэкенд общего доступа для %s не найден", "Sharing failed, because the user %s is the original sharer" : "Не удалось поделиться, потому что пользователь %s владелец этого элемента", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Не удалось поделиться %s, права превышают предоставленные права доступа %s", "Sharing %s failed, because resharing is not allowed" : "Не удалось поделиться %s, повторное открытие доступа запрещено", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Не удалось поделиться %s, бэкенд общего доступа не нашел путь до %s", "Sharing %s failed, because the file could not be found in the file cache" : "Не удалось поделиться %s, элемент не найден в файловом кеше.", "Can’t increase permissions of %s" : "Невозможно увеличить права доступа для %s", "Files can’t be shared with delete permissions" : "Файлы не могут иметь общий доступ с правами на удаление", "Files can’t be shared with create permissions" : "Файлы не могут иметь общий доступ с правами на создание", "Expiration date is in the past" : "Дата окончания срока действия уже прошла", "Can’t set expiration date more than %s days in the future" : "Невозможно установить дату окончания срока действия более %s дней", "%s shared »%s« with you" : "%s поделился »%s« с вами", "%s shared »%s« with you." : "%s поделился »%s« с вами.", "Click the button below to open it." : "Для открытия нажмите на кнопку ниже.", "Open »%s«" : "Открыть »%s«", "%s via %s" : "%s через %s", "The requested share does not exist anymore" : "Запрошенный общий ресурс более не существует.", "Could not find category \"%s\"" : "Категория «%s» не найдена", "Sunday" : "Воскресенье", "Monday" : "Понедельник", "Tuesday" : "Вторник", "Wednesday" : "Среда", "Thursday" : "Четверг", "Friday" : "Пятница", "Saturday" : "Суббота", "Sun." : "Вс.", "Mon." : "Пн.", "Tue." : "Вт.", "Wed." : "Ср.", "Thu." : "Чт.", "Fri." : "Пт.", "Sat." : "Сб.", "Su" : "Вс", "Mo" : "Пн", "Tu" : "Вт", "We" : "Ср", "Th" : "Чт", "Fr" : "Пт", "Sa" : "Сб", "January" : "Январь", "February" : "Февраль", "March" : "Март", "April" : "Апрель", "May" : "Май", "June" : "Июнь", "July" : "Июль", "August" : "Август", "September" : "Сентябрь", "October" : "Октябрь", "November" : "Ноябрь", "December" : "Декабрь", "Jan." : "Янв.", "Feb." : "Фев.", "Mar." : "Мар.", "Apr." : "Апр.", "May." : "Май", "Jun." : "Июн.", "Jul." : "Июл.", "Aug." : "Авг.", "Sep." : "Сен.", "Oct." : "Окт.", "Nov." : "Нояб.", "Dec." : "Дек.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "В составе имени пользователя допускаются следующие символы: «a–z», «A–Z», «0–9» и «_.@-'»", "A valid username must be provided" : "Укажите допустимое имя пользователя", "Username contains whitespace at the beginning or at the end" : "Имя пользователя содержит пробел в начале или в конце", "Username must not consist of dots only" : "Имя пользователя должно состоять не только из точек", "A valid password must be provided" : "Укажите допустимый пароль", "The username is already being used" : "Имя пользователя уже используется", "Could not create user" : "Не удалось создать пользователя", "User disabled" : "Пользователь отключен", "Login canceled by app" : "Вход отменен приложением", "No app name specified" : "Не указано имя приложения", "App '%s' could not be installed!" : "Приложение '%s' не может быть установлено!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Приложение «%s» не может быть установлено, так как следующие зависимости не выполнены: %s", "a safe home for all your data" : "надёжный дом для всех ваших данных", "File is currently busy, please try again later" : "Файл в данный момент используется, повторите попытку позже.", "Can't read file" : "Не удается прочитать файл", "Application is not enabled" : "Приложение не разрешено", "Authentication error" : "Ошибка аутентификации", "Token expired. Please reload page." : "Токен просрочен. Перезагрузите страницу.", "Unknown user" : "Неизвестный пользователь", "No database drivers (sqlite, mysql, or postgresql) installed." : "Не установлены драйвера баз данных (sqlite, mysql или postgresql)", "Cannot write into \"config\" directory" : "Запись в каталог «config» невозможна", "Cannot write into \"apps\" directory" : "Запись в каталог «app» невозможна", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог приложений или отключив магазин приложений в файле конфигурации. Смотрите %s", "Cannot create \"data\" directory" : "Невозможно создать каталог «data»", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в корневой каталог. Смотрите %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Разрешения обычно можно исправить, предоставив веб-серверу право на запись в корневой каталог. Смотрите %s.", "Setting locale to %s failed" : "Установка локали %s не удалась", "Please install one of these locales on your system and restart your webserver." : "Установите один из этих языковых пакетов на вашу систему и перезапустите веб-сервер.", "Please ask your server administrator to install the module." : "Пожалуйста, попростите администратора сервера установить модуль.", "PHP module %s not installed." : "Не установлен PHP-модуль %s.", "PHP setting \"%s\" is not set to \"%s\"." : "Параметру PHP «%s» не присвоено значение «%s».", "Adjusting this setting in php.ini will make Nextcloud run again" : "Настройка этого параметра в php.ini поможет Nextcloud работать снова", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload установлен в «%s», при этом требуется «0»", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Чтобы исправить эту проблему установите параметр <code>mbstring.func_overload</code> в значение <code>0</code> в php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Требуется как минимум libxml2 версии 2.7.0. На данный момент установлена %s.", "To fix this issue update your libxml2 version and restart your web server." : "Для исправления этой ошибки обновите версию libxml2 и перезапустите ваш веб-сервер.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Очевидно, PHP настроен на вычищение блоков встроенной документации. Это сделает несколько центральных приложений недоступными.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Возможно это вызвано кешем/ускорителем вроде Zend OPcache или eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Модули PHP были установлены, но они все еще перечислены как недостающие?", "Please ask your server administrator to restart the web server." : "Пожалуйста, попросите вашего администратора перезапустить веб-сервер.", "PostgreSQL >= 9 required" : "Требуется PostgreSQL >= 9", "Please upgrade your database version" : "Обновите базу данных", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Измените права доступа на 0770, чтобы другие пользователи не могли получить список файлов этого каталога.", "Your data directory is readable by other users" : "Каталог данных доступен для чтения другим пользователям", "Your data directory must be an absolute path" : "Каталог данных должен быть указан в виде абсолютного пути", "Check the value of \"datadirectory\" in your configuration" : "Проверьте значение «datadirectory» в настройках.", "Your data directory is invalid" : "Каталог данных не верен", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Убедитесь, что в корне каталога данных присутствует файл «.ocdata».", "Could not obtain lock type %d on \"%s\"." : "Не удалось получить блокировку типа %d для «%s»", "Storage unauthorized. %s" : "Хранилище неавторизовано. %s", "Storage incomplete configuration. %s" : "Неполная конфигурация хранилища. %s", "Storage connection error. %s" : "Ошибка подключения к хранилищу. %s", "Storage is temporarily not available" : "Хранилище временно недоступно", "Storage connection timeout. %s" : "Истекло время ожидания подключения к хранилищу. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Обычно это можно исправить %sпредоставив веб-серверу права на запись в каталоге конфигурации%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модуль с ID %s не существует. Пожалуйста включите его в настройках приложений или обратитесь к администратору.", "Server settings" : "Настройки сервера", "DB Error: \"%s\"" : "Ошибка БД: «%s»", "Offending command was: \"%s\"" : "Вызываемая команда была: «%s»", "You need to enter either an existing account or the administrator." : "Вы должны войти или в существующий аккаунт или под администратором.", "Offending command was: \"%s\", name: %s, password: %s" : "Вызываемая команда была: «%s», имя: %s, пароль: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Не удалось настроить права доступа для %s, указанные права доступа превышают предоставленные для %s", "Setting permissions for %s failed, because the item was not found" : "Не удалось настроить права доступа для %s, элемент не найден.", "Cannot clear expiration date. Shares are required to have an expiration date." : "Невозможно очистить дату истечения срока действия. Общие ресурсы должны иметь срок годности.", "Cannot increase permissions of %s" : "Невозможно увеличить права доступа для %s", "Files can't be shared with delete permissions" : "Файлы не могут иметь общий доступ с правами на удаление", "Files can't be shared with create permissions" : "Файлы не могут иметь общий доступ с правами на создание", "Cannot set expiration date more than %s days in the future" : "Невозможно установить дату окончания срока действия более %s дней", "Personal" : "Личное", "Admin" : "Администрирование", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Обычно это можно исправить, %sпредоставив веб-серверу права на запись в каталог приложений%s или отключив магазин приложений в файле конфигурации.", "Cannot create \"data\" directory (%s)" : "Невозможно создать каталог «data» (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Обычно это можно исправить <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">предоставив веб-серверу права на запись в корневом каталоге</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Обычно это можно исправить, %sпредоставив веб-серверу права на запись в корневой каталог%s.", "Data directory (%s) is readable by other users" : "Каталог данных (%s) доступен для чтения другим пользователям", "Data directory (%s) must be an absolute path" : "Каталог данных (%s) должен быть абсолютным путём", "Data directory (%s) is invalid" : "Каталог данных (%s) не верен", "Please check that the data directory contains a file \".ocdata\" in its root." : "Убедитесь, что файл «.ocdata» присутствует в корне каталога данных." }, "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"); l10n/ko.js 0000604 00000051622 15247130447 0006276 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "\"config\" 디렉터리에 기록할 수 없습니다!", "This can usually be fixed by giving the webserver write access to the config directory" : "config 디렉터리에 웹 서버 쓰기 권한을 주면 해결됩니다", "See %s" : "%s 보기", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "앱 %1$s의 파일이 올바르게 교체되지 않았습니다. 서버와 호환되는 버전인지 확인하십시오.", "Sample configuration detected" : "예제 설정 감지됨", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "예제 설정이 복사된 것 같습니다. 올바르게 작동하지 않을 수도 있기 때문에 지원되지 않습니다. config.php를 변경하기 전 문서를 읽어 보십시오", "%1$s and %2$s" : "%1$s 및 %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s 및 %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s 및 %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s 및 %5$s", "Enterprise bundle" : "엔터프라이즈 번들", "Groupware bundle" : "그룹웨어 번들", "Social sharing bundle" : "소셜 공유 번들", "PHP %s or higher is required." : "PHP 버전 %s 이상이 필요합니다.", "PHP with a version lower than %s is required." : "PHP 버전 %s 미만이 필요합니다.", "%sbit or higher PHP required." : "%s비트 이상의 PHP가 필요합니다.", "Following databases are supported: %s" : "다음 데이터베이스를 지원합니다: %s", "The command line tool %s could not be found" : "명령행 도구 %s을(를) 찾을 수 없습니다", "The library %s is not available." : "%s 라이브러리를 사용할 수 없습니다.", "Library %s with a version higher than %s is required - available version %s." : "%s 라이브러리의 버전 %s 이상이 필요합니다. 사용 가능한 버전은 %s입니다.", "Library %s with a version lower than %s is required - available version %s." : "%s 라이브러리의 버전 %s 미만이 필요합니다. 사용 가능한 버전은 %s입니다.", "Following platforms are supported: %s" : "다음 플랫폼을 지원합니다: %s", "Server version %s or higher is required." : "서버 버전 %s 이상이 필요합니다.", "Server version %s or lower is required." : "서버 버전 %s 미만이 필요합니다.", "Unknown filetype" : "알 수 없는 파일 형식", "Invalid image" : "잘못된 사진", "Avatar image is not square" : "아바타 사진이 정사각형이 아님", "today" : "오늘", "yesterday" : "어제", "_%n day ago_::_%n days ago_" : ["%n일 전"], "last month" : "지난 달", "_%n month ago_::_%n months ago_" : ["%n달 전 "], "last year" : "작년", "_%n year ago_::_%n years ago_" : ["%n년 전"], "_%n hour ago_::_%n hours ago_" : ["%n시간 전"], "_%n minute ago_::_%n minutes ago_" : ["%n분 전"], "seconds ago" : "초 전", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID: %s인 모듈이 존재하지 않습니다. 앱 설정에서 확인하거나 시스템 관리자에게 연락하십시오.", "File name is a reserved word" : "파일 이름이 예약된 단어임", "File name contains at least one invalid character" : "파일 이름에 잘못된 글자가 한 자 이상 있음", "File name is too long" : "파일 이름이 너무 김", "Dot files are not allowed" : "점으로 시작하는 파일은 허용되지 않음", "Empty filename is not allowed" : "파일 이름을 비워 둘 수 없음", "App \"%s\" cannot be installed because appinfo file cannot be read." : "appinfo 파일을 읽을 수 없어서 앱 \"%s\"을(를) 설치할 수 없습니다.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "이 서버 버전과 호환되지 않아서 앱 \"%s\"을(를) 설치할 수 없습니다", "This is an automatically sent email, please do not reply." : "자동으로 전송한 이메일입니다. 답장하지 마십시오.", "Help" : "도움말", "Apps" : "앱", "Log out" : "로그아웃", "Users" : "사용자", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "기본 설정", "Sharing" : "공유", "Security" : "보안", "Encryption" : "암호화", "Additional settings" : "고급 설정", "Tips & tricks" : "팁과 추가 정보", "%s enter the database username and name." : "%s 데이터베이스 사용자 이름과 이름을 입력해 주십시오.", "%s enter the database username." : "%s 데이터베이스 사용자 이름을 입력해 주십시오.", "%s enter the database name." : "%s 데이터베이스 이름을 입력하십시오.", "%s you may not use dots in the database name" : "%s 데이터베이스 이름에는 마침표를 사용할 수 없습니다", "Oracle connection could not be established" : "Oracle 연결을 수립할 수 없습니다.", "Oracle username and/or password not valid" : "Oracle 사용자 이름이나 암호가 잘못되었습니다.", "PostgreSQL username and/or password not valid" : "PostgreSQL 사용자 이름 또는 암호가 잘못되었습니다", "You need to enter details of an existing account." : "존재하는 계정 정보를 입력해야 합니다.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X은 지원하지 않으며 %s이(가) 이 플랫폼에서 올바르게 작동하지 않을 수도 있습니다. 본인 책임으로 사용하십시오! ", "For the best results, please consider using a GNU/Linux server instead." : "더 좋은 결과를 얻으려면 GNU/Linux 서버를 사용하는 것을 권장합니다.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "%s 인스턴스가 32비트 PHP 환경에서 실행 중이고 php.ini에 open_basedir이 설정되어 있습니다. 4GB 이상의 파일 처리에 문제가 생길 수 있으므로 추천하지 않습니다.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "php.ini의 open_basedir 설정을 삭제하거나 64비트 PHP로 전환하십시오.", "Set an admin username." : "관리자의 사용자 이름을 설정합니다.", "Set an admin password." : "관리자의 암호를 설정합니다.", "Can't create or write into the data directory %s" : "데이터 디렉터리 %s을(를) 만들거나 기록할 수 없음", "Invalid Federated Cloud ID" : "잘못된 연합 클라우드 ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "%s을(를) 공유할 수 없습니다. 백엔드에서 %i 형식의 공유를 허용하지 않습니다", "Sharing %s failed, because the file does not exist" : "%s을(를) 공유할 수 없습니다. 파일이 존재하지 않습니다", "You are not allowed to share %s" : "%s을(를) 공유할 수 있는 권한이 없습니다", "Sharing %s failed, because you can not share with yourself" : "%s을(를) 공유할 수 없습니다. 자기 자신과 공유할 수 없습니다", "Sharing %s failed, because the user %s does not exist" : "%s을(를) 공유할 수 없습니다. 사용자 %s이(가) 존재하지 않습니다", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s을(를) 공유할 수 없습니다. 사용자 %s 님은 %s 님이 회원인 어떠한 그룹에도 속해 있지 않습니다", "Sharing %s failed, because this item is already shared with %s" : "%s을(를) 공유할 수 없습니다. 이미 %s 님과 공유되어 있습니다", "Sharing %s failed, because this item is already shared with user %s" : "%s을(를) 공유할 수 없습니다. 이 항목을 이미 %s 님과 공유하고 있습니다", "Sharing %s failed, because the group %s does not exist" : "%s을(를) 공유할 수 없습니다. 그룹 %s이(가) 존재하지 않습니다", "Sharing %s failed, because %s is not a member of the group %s" : "%s을(를) 공유할 수 없습니다. %s 님이 그룹 %s의 구성원이 아닙니다", "You need to provide a password to create a public link, only protected links are allowed" : "공개 링크를 만들려면 암호를 입력해야 합니다. 보호된 링크만 사용 가능합니다", "Sharing %s failed, because sharing with links is not allowed" : "%s을(를) 공유할 수 없습니다. 링크 공유가 허용되지 않았습니다", "Not allowed to create a federated share with the same user" : "같은 사용자와 연합 공유를 만들 수 없음", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s을(를) 공유할 수 없습니다. %s을(를) 찾을 수 없습니다. 서버에 접근하지 못할 수도 있습니다.", "Share type %s is not valid for %s" : "공유 형식 %s을(를) %s에 대해서 사용할 수 없음", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "만료 날짜를 설정할 수 없습니다. 최대 공유 허용 기한이 %s입니다.", "Cannot set expiration date. Expiration date is in the past" : "만료 날짜를 설정할 수 없습니다. 만료 날짜가 과거입니다", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "공유 백엔드 %s에서 OCP\\Share_Backend 인터페이스를 구현해야 함", "Sharing backend %s not found" : "공유 백엔드 %s을(를) 찾을 수 없음", "Sharing backend for %s not found" : "%s의 공유 백엔드를 찾을 수 없음", "Sharing failed, because the user %s is the original sharer" : "공유할 수 없습니다. 사용자 %s이(가) 원 공유자입니다", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "%s을(를) 공유할 수 없습니다. %s 님에게 허용된 것 이상의 권한을 필요로 합니다", "Sharing %s failed, because resharing is not allowed" : "%s을(를) 공유할 수 없습니다. 다시 공유할 수 없습니다", "Sharing %s failed, because the sharing backend for %s could not find its source" : "%s을(를) 공유할 수 없습니다. %s의 공유 백엔드에서 원본 파일을 찾을 수 없습니다", "Sharing %s failed, because the file could not be found in the file cache" : "%s을(를) 공유할 수 없습니다. 파일 캐시에서 찾을 수 없습니다", "Expiration date is in the past" : "만료 날짜가 과거입니다", "%s shared »%s« with you" : "%s 님이 %s을(를) 공유했습니다", "%s via %s" : "%s(%s 경유)", "Could not find category \"%s\"" : "분류 \"%s\"을(를) 찾을 수 없습니다", "Sunday" : "일요일", "Monday" : "월요일", "Tuesday" : "화요일", "Wednesday" : "수요일", "Thursday" : "목요일", "Friday" : "금요일", "Saturday" : "토요일", "Sun." : "일", "Mon." : "월", "Tue." : "화", "Wed." : "수", "Thu." : "목", "Fri." : "금", "Sat." : "토", "Su" : "일", "Mo" : "월", "Tu" : "화", "We" : "수", "Th" : "목", "Fr" : "금", "Sa" : "토", "January" : "1월", "February" : "2월", "March" : "3월", "April" : "4월", "May" : "5월", "June" : "6월", "July" : "7월", "August" : "8월", "September" : "9월", "October" : "10월", "November" : "11월", "December" : "12월", "Jan." : "1월", "Feb." : "2월", "Mar." : "3월", "Apr." : "4월", "May." : "5월", "Jun." : "6월", "Jul." : "7월", "Aug." : "8월", "Sep." : "9월", "Oct." : "10월", "Nov." : "11월", "Dec." : "12월", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "다음 문자만 이름에 사용할 수 있습니다: \"a-z\", \"A-Z\", \"0-9\", 및 \"_.@-'\"", "A valid username must be provided" : "올바른 사용자 이름을 입력해야 합니다", "Username contains whitespace at the beginning or at the end" : "사용자 이름의 시작이나 끝에 공백이 있습니다", "Username must not consist of dots only" : "사용자 이름에 마침표만 있으면 안 됩니다", "A valid password must be provided" : "올바른 암호를 입력해야 합니다", "The username is already being used" : "사용자 이름이 이미 존재합니다", "User disabled" : "사용자 비활성화", "Login canceled by app" : "앱 로그인 취소", "No app name specified" : "앱 이름이 지정되지 않았음", "App '%s' could not be installed!" : "앱 '%s'을(를) 설치할 수 없습니다!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "앱 \"%s\"의 다음 의존성을 만족하지 못하므로 설치할 수 없습니다: %s", "a safe home for all your data" : "내 모든 데이터의 안전한 저장소", "File is currently busy, please try again later" : "파일이 현재 사용 중, 나중에 다시 시도하십시오", "Can't read file" : "파일을 읽을 수 없음", "Application is not enabled" : "앱이 활성화되지 않았습니다", "Authentication error" : "인증 오류", "Token expired. Please reload page." : "토큰이 만료되었습니다. 페이지를 새로 고치십시오.", "Unknown user" : "알려지지 않은 사용자", "No database drivers (sqlite, mysql, or postgresql) installed." : "데이터베이스 드라이버(sqlite, mysql, postgresql)가 설치되지 않았습니다.", "Cannot write into \"config\" directory" : "\"config\" 디렉터리에 기록할 수 없습니다", "Cannot write into \"apps\" directory" : "\"apps\" 디렉터리에 기록할 수 없습니다", "Cannot create \"data\" directory" : "\"data\" 디렉터리를 만들 수 없음", "Setting locale to %s failed" : "로캘을 %s(으)로 설정할 수 없음", "Please install one of these locales on your system and restart your webserver." : "다음 중 하나 이상의 로캘을 시스템에 설치하고 웹 서버를 다시 시작하십시오.", "Please ask your server administrator to install the module." : "서버 관리자에게 모듈 설치를 요청하십시오.", "PHP module %s not installed." : "PHP 모듈 %s이(가) 설치되지 않았습니다.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP 설정 \"%s\"이(가) \"%s\"(으)로 설정되어 있지 않습니다.", "Adjusting this setting in php.ini will make Nextcloud run again" : "php.ini 파일에서 설정을 변경하면 Nextcloud가 다시 실행됩니다", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload 값이 \"%s\"(으)로 설정되어 있으나 \"0\"으로 설정해야 함", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "이 문제를 해결하려면 php.ini에서 <code>mbstring.func_overload</code> 값을 <code>0</code>으로 설정하십시오", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 이상이 필요합니다. 현재 버전은 %s입니다.", "To fix this issue update your libxml2 version and restart your web server." : "이 문제를 해결하려면 libxml2 버전을 업데이트하고 웹 서버를 다시 시작하십시오.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP에서 인라인 문서 블록을 삭제하도록 설정되어 있습니다. 일부 코어 앱을 사용하지 못할 수도 있습니다.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Zend OPcache, eAccelerator 같은 캐시/가속기 문제일 수도 있습니다.", "PHP modules have been installed, but they are still listed as missing?" : "PHP 모듈이 설치되었지만 여전히 없는 것으로 나타납니까?", "Please ask your server administrator to restart the web server." : "서버 관리자에게 웹 서버 재시작을 요청하십시오.", "PostgreSQL >= 9 required" : "PostgreSQL 버전 9 이상이 필요합니다", "Please upgrade your database version" : "데이터베이스 버전을 업그레이드 하십시오", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "권한을 0770으로 변경하여 다른 사용자가 읽을 수 없도록 하십시오.", "Your data directory is readable by other users" : "내 데이터 디렉터리를 다른 사용자가 읽을 수 있음", "Your data directory must be an absolute path" : "내 데이터 디렉터리는 절대 경로여야 함", "Check the value of \"datadirectory\" in your configuration" : "설정 중 \"datadirectory\" 값을 확인하십시오", "Your data directory is invalid" : "내 데이터 디렉터리가 잘못됨", "Could not obtain lock type %d on \"%s\"." : "잠금 형식 %d을(를) \"%s\"에 대해 얻을 수 없습니다.", "Storage unauthorized. %s" : "저장소가 인증되지 않았습니다. %s", "Storage incomplete configuration. %s" : "저장소 설정이 완전하지 않습니다. %s", "Storage connection error. %s" : "저장소 연결 오류. %s", "Storage is temporarily not available" : "저장소를 임시로 사용할 수 없음", "Storage connection timeout. %s" : "저장소 연결 시간 초과. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "%sconfig 디렉터리에 웹 서버 쓰기 권한%s을 주면 해결됩니다.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID: %s인 모듈이 존재하지 않습니다. 앱 설정에서 활성화하거나 관리자에게 연락하십시오.", "Server settings" : "서버 설정", "DB Error: \"%s\"" : "DB 오류: \"%s\"", "Offending command was: \"%s\"" : "잘못된 명령: \"%s\"", "You need to enter either an existing account or the administrator." : "기존 계정이나 administrator(관리자)를 입력해야 합니다.", "Offending command was: \"%s\", name: %s, password: %s" : "잘못된 명령: \"%s\", 이름: %s, 암호: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "%s의 권한을 설정할 수 없습니다. %s 님에게 허용된 것 이상의 권한을 필요로 합니다", "Setting permissions for %s failed, because the item was not found" : "%s의 권한을 설정할 수 없습니다. 항목을 찾을 수 없습니다", "Cannot clear expiration date. Shares are required to have an expiration date." : "만료 날짜를 비워 둘 수 없습니다. 공유되는 항목에는 만료 날짜가 필요합니다.", "Cannot increase permissions of %s" : "%s의 권한을 늘릴 수 없습니다", "Files can't be shared with delete permissions" : "파일을 삭제 권한으로 공유할 수 없습니다", "Files can't be shared with create permissions" : "파일을 생성 권한으로 공유할 수 없습니다", "Cannot set expiration date more than %s days in the future" : "만료 날짜를 %s일 이상 이후로 설정할 수 없습니다", "Personal" : "개인", "Admin" : "관리자", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "%sapps 디렉터리에 웹 서버 쓰기 권한%s을 주거나 설정 파일에서 앱 스토어를 비활성화하면 해결됩니다.", "Cannot create \"data\" directory (%s)" : "\"data\" 디렉터리를 만들 수 없음(%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">루트 디렉터리에 웹 서버 쓰기 권한</a>을 주면 해결됩니다.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "%s루트 디렉터리에 웹 서버 쓰기 권한%s을 주면 해결됩니다.", "Data directory (%s) is readable by other users" : "데이터 디렉터리(%s)를 다른 사용자가 읽을 수 있음", "Data directory (%s) must be an absolute path" : "데이터 디렉터리(%s)는 반드시 절대 경로여야 함", "Data directory (%s) is invalid" : "데이터 디렉터리(%s)가 잘못됨", "Please check that the data directory contains a file \".ocdata\" in its root." : "데이터 디렉터리의 최상위 경로에 \".ocdata\" 파일이 있는지 확인하십시오." }, "nplurals=1; plural=0;"); l10n/el.json 0000604 00000077610 15247130447 0006627 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Αδυναμία εγγραφής στον κατάλογο \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Αυτό μπορεί συνήθως να διορθωθεί παρέχοντας δικαιώματα εγγραφής για το φάκελο config στο διακομιστή δικτύου", "See %s" : "Δείτε %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Αυτό μπορεί συνήθως να διορθωθεί δίνοντας στον διακομιστή γραπτή πρόσβαση στον κατάλογο εκχώρησης. Βλέπε%s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Τα αρχεία της εφαρμογής% $ 1s δεν αντικαταστάθηκαν σωστά. Βεβαιωθείτε ότι πρόκειται για μια έκδοση που είναι συμβατή με το διακομιστή.", "Sample configuration detected" : "Ανιχνεύθηκε δείγμα εγκατάστασης", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Έχει ανιχνευθεί ότι το δείγμα εγκατάστασης έχει αντιγραφεί. Αυτό μπορεί να σπάσει την εγκατάστασή σας και δεν υποστηρίζεται. Παρακαλώ διαβάστε την τεκμηρίωση πριν εκτελέσετε αλλαγές στο config.php", "%1$s and %2$s" : "%1$s και %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s και %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s και %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s και %5$s", "Education Edition" : "Εκπαιδευτική Έκδοση", "Enterprise bundle" : "Πακέτο επιχειρήσεων", "Groupware bundle" : "Ομάδα δέσμης", "Social sharing bundle" : "Πακέτο κοινωνικού διαμοιρασμού", "PHP %s or higher is required." : "PHP %s ή νεώτερη απαιτείται.", "PHP with a version lower than %s is required." : "Απαιτείται PHP παλαιότερη από την έκδοση %s.", "%sbit or higher PHP required." : "%sbit απαιτείται νεώτερη έκδοση PHP.", "Following databases are supported: %s" : " Υποστηρίζονται οι ακόλουθες βάσεις δεδομένων: %s", "The command line tool %s could not be found" : "Το εργαλείο γραμμής εντολών %s δεν μπορεί να βρεθεί", "The library %s is not available." : "Το %s της βιβλιοθήκης δεν είναι διαθέσιμο.", "Library %s with a version higher than %s is required - available version %s." : "Απαιτείται βιβλιοθήκη %s νεότερη από την έκδοση %s - διαθέσιμη έκδοση %s ", "Library %s with a version lower than %s is required - available version %s." : "Απαιτείται βιβλιοθήκη %s παλαιότερη από την έκδοση %s - διαθέσιμη έκδοση %s ", "Following platforms are supported: %s" : "Οι ακόλουθες πλατφόρμες υποστηρίζονται: %s", "Server version %s or higher is required." : "Απαιτείται έκδοση διακομιστή %s ή νεότερη.", "Server version %s or lower is required." : "Απαιτείται έκδοση διακομιστή %s ή παλαιότερη.", "Unknown filetype" : "Άγνωστος τύπος αρχείου", "Invalid image" : "Μη έγκυρη εικόνα", "Avatar image is not square" : "Η εικόνα του άβαταρ δεν είναι τετράγωνη", "today" : "σήμερα", "yesterday" : "χτες", "_%n day ago_::_%n days ago_" : ["%n ημέρα πριν","%n ημέρες πριν"], "last month" : "τελευταίο μήνα", "_%n month ago_::_%n months ago_" : ["πριν %n μήνα","πριν %n μήνες"], "last year" : "τελευταίο χρόνο", "_%n year ago_::_%n years ago_" : ["%n χρόνο πριν","%n χρόνια πριν"], "_%n hour ago_::_%n hours ago_" : ["%nώρα πριν","%nώρες πριν"], "_%n minute ago_::_%n minutes ago_" : ["%nλεπτό πριν","%nλεπτά πριν"], "seconds ago" : "δευτερόλεπτα πριν", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Το άρθρωμα με ID: %sδεν υπάρχει. Παρακαλούμε ενεργοποιήστε το στις ρυθμίσεις των εφαρμογών σας ή επικοινωνήστε με τον διαχειριστή.", "File name is a reserved word" : "Το όνομα αρχείου είναι λέξη που έχει δεσμευτεί", "File name contains at least one invalid character" : "Το όνομα αρχείου περιέχει έναν τουλάχιστον μη έγκυρο χαρακτήρα", "File name is too long" : "Το όνομα αρχείου είνια πολύ μεγάλο", "Dot files are not allowed" : "Δεν επιτρέπονται αρχεία που ξεκινούν από τελεία - Dot ", "Empty filename is not allowed" : "Δεν επιτρέπεται άδειο όνομα αρχείου", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Η εφαρμογή \"%s\" δεν μπορεί να εγκατασταθεί διότι δεν είναι δυνατή η ανάγνωση του αρχείου appinfo.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Η εφαρμογή \"%s\" δεν μπορεί να εγκατασταθεί διότι δεν είναι συμβατή με την έκδοση του διακομιστή.", "This is an automatically sent email, please do not reply." : "Αυτό είναι ένα μήνυμα ηλεκτρονικού ταχυδρομείου που στάλθηκε αυτόματα, παρακαλούμε μην απαντήσετε.", "Help" : "Βοήθεια", "Apps" : "Εφαρμογές", "Settings" : "Ρυθμίσεις", "Log out" : "Έξοδος", "Users" : "Χρήστες", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Βασικές ρυθμίσεις", "Sharing" : "Διαμοιρασμός", "Security" : "Ασφάλεια", "Encryption" : "Κρυπτογράφηση", "Additional settings" : "Επιπρόσθετες ρυθμίσεις", "Tips & tricks" : "Συμβουλές & κόλπα", "Personal info" : "Προσωπικές πληροφορίες", "Sync clients" : "Εφαρμογές συγχρονισμού", "Unlimited" : "Απεριόριστα", "__language_name__" : "__language_name__", "Verifying" : "Γίνεται επαλήθευση", "Verifying …" : "Γίνεται επαλήθευση ...", "Verify" : "Επαλήθευση", "%s enter the database username and name." : "%sπληκτρολογήστε όνομα χρήστη και όνομα βάσης δεδομένων.", "%s enter the database username." : "%s εισάγετε το όνομα χρήστη της βάσης δεδομένων.", "%s enter the database name." : "%s εισάγετε το όνομα της βάσης δεδομένων.", "%s you may not use dots in the database name" : "%s μάλλον δεν χρησιμοποιείτε τελείες στο όνομα της βάσης δεδομένων", "Oracle connection could not be established" : "Αδυναμία σύνδεσης Oracle", "Oracle username and/or password not valid" : "Μη έγκυρος χρήστης και/ή συνθηματικό της Oracle", "PostgreSQL username and/or password not valid" : "Μη έγκυρος χρήστης και/ή συνθηματικό της PostgreSQL", "You need to enter details of an existing account." : "Χρειάζεται να εισάγετε λεπτομέρειες από υπάρχον λογαριασμό.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Το Mac OS X δεν υποστηρίζεται και το %s δεν θα λειτουργήσει σωστά σε αυτή την πλατφόρμα. Χρησιμοποιείτε με δική σας ευθύνη!", "For the best results, please consider using a GNU/Linux server instead." : "Για καλύτερα αποτελέσματα, παρακαλούμε εξετάστε την μετατροπή σε έναν διακομιστή GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Φαίνεται ότι η εγκατάσταση %s εκτελείται σε περιβάλλον 32-bit PHP και η επιλογη open_basedir έχει ρυθμιστεί στο αρχείο php.ini. Αυτό θα οδηγήσει σε προβλήματα με αρχεία πάνω από 4 GB και δεν συνίσταται.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Παρακαλώ αφαιρέστε την ρύθμιση open_basedir μέσα στο αρχείο php.ini ή αλλάξτε σε 64-bit PHP.", "Set an admin username." : "Εισάγετε όνομα χρήστη διαχειριστή.", "Set an admin password." : "Εισάγετε συνθηματικό διαχειριστή.", "Can't create or write into the data directory %s" : "Αδύνατη η δημιουργία ή συγγραφή στον κατάλογο δεδομένων %s", "Invalid Federated Cloud ID" : "Μη έγκυρο Federated Cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Αποτυχία διαμοιρασμού %s, γιατί το σύστημα υποστήριξης δεν επιτρέπει κοινόχρηστα τύπου %i", "Sharing %s failed, because the file does not exist" : "Ο διαμοιρασμός του %s απέτυχε, γιατί το αρχείο δεν υπάρχει", "You are not allowed to share %s" : "Δεν επιτρέπεται να διαμοιράσετε τον πόρο %s", "Sharing %s failed, because you can not share with yourself" : "Ο διαμοιρασμός του %s απέτυχε, γιατί δεν μπορείτε να διαμοιραστείτε με τον εαυτό σας.", "Sharing %s failed, because the user %s does not exist" : "Ο διαμοιρασμός του %s απέτυχε, γιατί ο χρήστης %s δεν υπάρχει", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Ο διαμοιρασμός του %s απέτυχε, γιατί ο χρήστης %s δεν είναι μέλος καμίας ομάδας στην οποία ο χρήστης %s είναι μέλος", "Sharing %s failed, because this item is already shared with %s" : "Ο διαμοιρασμός του %s απέτυχε, γιατί το αντικείμενο είναι διαμοιρασμένο ήδη με τον χρήστη %s", "Sharing %s failed, because this item is already shared with user %s" : "Αποτυχία διαμοιρασμού με %s, διότι αυτό το αντικείμενο διαμοιράζεται ήδη με τον χρήστη %s", "Sharing %s failed, because the group %s does not exist" : "Ο διαμοιρασμός του %s απέτυχε, γιατί η ομάδα χρηστών %s δεν υπάρχει", "Sharing %s failed, because %s is not a member of the group %s" : "Ο διαμοιρασμός του %s απέτυχε, γιατί ο χρήστης %s δεν είναι μέλος της ομάδας %s", "You need to provide a password to create a public link, only protected links are allowed" : "Πρέπει να εισάγετε έναν κωδικό για να δημιουργήσετε έναν δημόσιο σύνδεσμο. Μόνο προστατευμένοι σύνδεσμοι επιτρέπονται", "Sharing %s failed, because sharing with links is not allowed" : "Ο διαμοιρασμός του %s απέτυχε, γιατί δεν επιτρέπεται ο διαμοιρασμός με συνδέσμους", "Not allowed to create a federated share with the same user" : "Δεν επιτρέπεται η δημιουργία federated διαμοιρασμού με τον ίδιο χρήστη", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Αποτυχία διαμοιρασμού %s, δεν βρέθηκε το %s, μπορεί ο διακομιστής να είναι προσωρινά απροσπέλαστος.", "Share type %s is not valid for %s" : "Ο τύπος διαμοιρασμού %s δεν είναι έγκυρος για το %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Δεν μπορεί να οριστεί ημερομηνία λήξης. Οι κοινοποιήσεις δεν μπορεί να λήγουν αργότερα από %s αφού έχουν διαμοιραστεί.", "Cannot set expiration date. Expiration date is in the past" : "Δεν μπορεί να οριστεί ημερομηνία λήξης. Η ημερομηνία λήξης είναι στο παρελθόν", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Το σύστημα διαμοιρασμού %s πρέπει να υλοποιεί την διεπαφή OCP\\Share_Backend", "Sharing backend %s not found" : "Το σύστημα διαμοιρασμού %s δεν βρέθηκε", "Sharing backend for %s not found" : "Το σύστημα διαμοιρασμού για το %s δεν βρέθηκε", "Sharing failed, because the user %s is the original sharer" : "Ο διαμοιρασμός του %s απέτυχε, γιατί το αντικείμενο είναι διαμοιρασμένο αρχικά από τον ίδιο χρήστη.", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Ο διαμοιρασμός του %s απέτυχε, γιατί τα δικαιώματα υπερτερούν αυτά που είναι ορισμένα για το %s", "Sharing %s failed, because resharing is not allowed" : "Ο διαμοιρασμός του %s απέτυχε, γιατί δεν επιτρέπεται ο επαναδιαμοιρασμός", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Ο διαμοιρασμός του %s απέτυχε, γιατί δεν ήταν δυνατό να εντοπίσει την πηγή το σύστημα διαμοιρασμού για το %s ", "Sharing %s failed, because the file could not be found in the file cache" : "Ο διαμοιρασμός του %s απέτυχε, γιατί το αρχείο δεν βρέθηκε στην προσωρινή αποθήκευση αρχείων", "Can’t increase permissions of %s" : "Αδυναμία αύξησης των δικαιωμάτων του %s", "Files can’t be shared with delete permissions" : "Δεν μπορεί να γίνει διαμοιρασμός αρχείων με δικαιώματα διαγραφής", "Files can’t be shared with create permissions" : "Δεν μπορεί να γίνει διαμοιρασμός αρχείων με δικαιώματα δημιουργίας", "Expiration date is in the past" : "Η ημερομηνία λήξης είναι στο παρελθόν", "Can’t set expiration date more than %s days in the future" : "Δεν είναι δυνατό να τεθεί η ημερομηνία λήξης σε περισσότερες από %s ημέρες στο μέλλον", "%s shared »%s« with you" : "Ο %s διαμοιράστηκε μαζί σας το »%s«", "%s via %s" : "%s μέσω %s", "The requested share does not exist anymore" : "Το διαμοιρασμένο που ζητήθηκε δεν υπάρχει πλέον", "Could not find category \"%s\"" : "Αδυναμία εύρεσης κατηγορίας \"%s\"", "Sunday" : "Κυριακή", "Monday" : "Δευτέρα", "Tuesday" : "Τρίτη", "Wednesday" : "Τετάρτη", "Thursday" : "Πέμπτη", "Friday" : "Παρασκευή", "Saturday" : "Σάββατο", "Sun." : "Κυρ.", "Mon." : "Δευ.", "Tue." : "Τρί.", "Wed." : "Τετ.", "Thu." : "Πέμ.", "Fri." : "Παρ.", "Sat." : "Σαβ.", "Su" : "Κυ", "Mo" : "Δε", "Tu" : "Τρ", "We" : "Τε", "Th" : "Πε", "Fr" : "Πα", "Sa" : "Σα", "January" : "Ιανουάριος", "February" : "Φεβρουάριος", "March" : "Μάρτιος", "April" : "Απρίλιος", "May" : "Μάϊος", "June" : "Ιούνιος", "July" : "Ιούλιος", "August" : "Αύγουστος", "September" : "Σεπτέμβριος", "October" : "Οκτώβριος", "November" : "Νοέμβριος", "December" : "Δεκέμβριος", "Jan." : "Ιαν.", "Feb." : "Φεβ.", "Mar." : "Μαρ.", "Apr." : "Απρ.", "May." : "Μαι.", "Jun." : "Ιουν.", "Jul." : "Ιουλ.", "Aug." : "Αυγ.", "Sep." : "Σεπ.", "Oct." : "Οκτ.", "Nov." : "Νοε.", "Dec." : "Δεκ.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Μόνο οι ακόλουθοι χαρακτήρες επιτρέπονται στο όνομα χρήστη; \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Πρέπει να δοθεί έγκυρο όνομα χρήστη", "Username contains whitespace at the beginning or at the end" : "Το όνομα χρήστη περιέχει κενό διάστημα στην αρχή ή στο τέλος", "Username must not consist of dots only" : "Το όνομα χρήστη δεν πρέπει να περιέχει μόνο τελείες", "A valid password must be provided" : "Πρέπει να δοθεί έγκυρο συνθηματικό", "The username is already being used" : "Το όνομα χρήστη είναι κατειλημμένο", "User disabled" : "Ο χρήστης απενεργοποιήθηκε", "Login canceled by app" : "Η είσοδος ακυρώθηκε από την εφαρμογή", "No app name specified" : "Δεν προδιορίστηκε όνομα εφαρμογής", "App '%s' could not be installed!" : "Δεν μπορεί να εγκατασταθεί η εφαρμογή '%s'!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Αυτή η εφαρμογή %s δεν μπορεί να εγκατασταθεί διότι δεν πληρούνται οι ακόλουθες εξαρτήσεις: %s", "a safe home for all your data" : "ένα ασφαλές μέρος για όλα τα δεδομένα σας", "File is currently busy, please try again later" : "Το αρχείο χρησιμοποιείται αυτή τη στιγμή, παρακαλώ προσπαθήστε αργότερα", "Can't read file" : "Αδυναμία ανάγνωσης αρχείου", "Application is not enabled" : "Δεν ενεργοποιήθηκε η εφαρμογή", "Authentication error" : "Σφάλμα πιστοποίησης", "Token expired. Please reload page." : "Το αναγνωριστικό έληξε. Παρακαλώ φορτώστε ξανά την σελίδα.", "Unknown user" : "Άγνωστος χρήστης", "No database drivers (sqlite, mysql, or postgresql) installed." : "Δεν βρέθηκαν εγκατεστημένοι οδηγοί βάσεων δεδομένων (sqlite, mysql, or postgresql).", "Cannot write into \"config\" directory" : "Αδυναμία εγγραφής στον κατάλογο \"config\"", "Cannot write into \"apps\" directory" : "Αδυναμία εγγραφής στον κατάλογο \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Αυτό συνήθως μπορεί να διορθωθεί δίνοντας δικαιώματα εγγραφής στον κατάλογο apps στον διακομιστή ιστού ή απενεργοποιώντας το appstore στο αρχείο διαμόρφωσης. Δείτε το %s", "Cannot create \"data\" directory" : "Αδυναμία δημιουργίας του καταλόγου \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Αυτό μπορεί συνήθως να διορθωθεί δίνοντας στον διακομιστή ιστού δικαιώματα εγγραφής στον βασικό κατάλογο. Δείτε το%s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Τα δικαιώματα πρόσβασης μπορούν συνήθως να διορθωθούν δίνοντας δικαιώματα εγγραφής στον βασικό κατάλογο στον διακομιστή ιστού. Δείτε το%s.", "Setting locale to %s failed" : "Ρύθμιση τοπικών ρυθμίσεων σε %s απέτυχε", "Please install one of these locales on your system and restart your webserver." : "Παρακαλώ να εγκαταστήσετε μία από αυτές τις τοπικές ρυθμίσεις στο σύστημά σας και να επανεκκινήσετε τον διακομιστή δικτύου σας.", "Please ask your server administrator to install the module." : "Παρακαλώ ζητήστε από το διαχειριστή του διακομιστή σας να εγκαταστήσει τη μονάδα.", "PHP module %s not installed." : "Η μονάδα %s PHP δεν είναι εγκατεστημένη. ", "PHP setting \"%s\" is not set to \"%s\"." : "Η ρύθμιση \"%s\"της PHP δεν είναι ορισμένη σε \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Προσαρμόζοντας αυτήν τη ρύθμιση στο php.ini το Nextcloud θα εκτελεστεί ξανά", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "Το mbstring.func_overload έχει ορισθεί σε \"%s\" αντί για την αναμενόμενη τιμή \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Για να διορθώσετε αυτό το πρόβλημα ορίστε το <code>mbstring.func_overload</code> σε <code>0</code> στο αρχείο php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Απαιτείται τουλάχιστον το libxml2 2.7.0. Αυτή τη στιγμή είναι εγκατεστημένο το %s.", "To fix this issue update your libxml2 version and restart your web server." : "Για να διορθώσετε το σφάλμα ενημερώστε την έκδοση του libxml2 και επανεκκινήστε τον διακομιστή.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Η PHP φαίνεται να είναι ρυθμισμένη ώστε να αφαιρεί inline doc blocks. Αυτό θα καταστήσει πολλές βασικές εφαρμογές μη διαθέσιμες.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Αυτό πιθανόν προκλήθηκε από προσωρινή μνήμη (cache)/επιταχυντή όπως τη Zend OPcache ή τον eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Κάποια αρθρώματα της PHP έχουν εγκατασταθεί, αλλά είναι ακόμα καταγεγραμμένες ως εκλιπόντα;", "Please ask your server administrator to restart the web server." : "Παρακαλώ ζητήστε από το διαχειριστή του διακομιστή σας να επανεκκινήσει το διακομιστή δικτύου σας.", "PostgreSQL >= 9 required" : "Απαιτείται PostgreSQL >= 9", "Please upgrade your database version" : "Παρακαλώ αναβαθμίστε την έκδοση της βάσης δεδομένων σας", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Παρακαλώ αλλάξτε τις ρυθμίσεις σε 0770 έτσι ώστε ο κατάλογος να μην μπορεί να προβάλλεται από άλλους χρήστες.", "Your data directory is readable by other users" : "Ο κατάλογος δεδομένων σας είναι διαθέσιμος προς ανάγνωση από άλλους χρήστες", "Your data directory must be an absolute path" : "Ο κατάλογος δεδομένων σας πρέπει να είναι απόλυτη διαδρομή", "Check the value of \"datadirectory\" in your configuration" : "Ελέγξτε την τιμή του \"Φάκελος Δεδομένων\" στις ρυθμίσεις σας", "Your data directory is invalid" : "Ο κατάλογος δεδομένων σας δεν είναι έγκυρος", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Εξασφαλίστε ότι υπάρχει ένα αρχείο με όνομα \".ocdata\" στον βασικό κατάλογο του καταλόγου δεδομένων.", "Could not obtain lock type %d on \"%s\"." : "Αδυναμία ανάκτησης τύπου κλειδιού %d στο \"%s\".", "Storage unauthorized. %s" : "Αποθηκευτικός χώρος χωρίς εξουσιοδότηση. %s", "Storage incomplete configuration. %s" : "Ελλιπής διαμόρφωση αποθηκευτικού χώρου. %s", "Storage connection error. %s" : "Σφάλμα σύνδεσης με αποθηκευτικό χώρο. %s", "Storage is temporarily not available" : "Μη διαθέσιμος χώρος αποθήκευσης προσωρινά", "Storage connection timeout. %s" : "Λήξη χρονικού ορίου σύνδεσης με αποθηκευτικό χώρο.%s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Αυτό μπορεί συνήθως να διορθωθεί %sπαρέχοντας δικαιώματα εγγραφής για το φάκελο config στο διακομιστή δικτύου%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Το άρθρωμα με id: %s δεν υπάρχει. Παρακαλώ ενεργοποιήστε το από τις ρυθμίσεις των εφαρμογών ή επικοινωνήστε με τον διαχειριστή.", "Server settings" : "Ρυθμίσεις διακομιστή", "DB Error: \"%s\"" : "Σφάλμα Βάσης Δεδομένων: \"%s\"", "Offending command was: \"%s\"" : "Η εντολη παραβατικοτητας ηταν: \"%s\"", "You need to enter either an existing account or the administrator." : "Χρειάζεται να εισάγετε είτε έναν υπάρχον λογαριασμό ή του διαχειριστή.", "Offending command was: \"%s\", name: %s, password: %s" : "Η εντολη παραβατικοτητας ηταν: \"%s\", ονομα: %s, κωδικος: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Ο ορισμός δικαιωμάτων για το %s απέτυχε, γιατί τα δικαιώματα υπερτερούν αυτά που είναι ορισμένα για το %s", "Setting permissions for %s failed, because the item was not found" : "Ο ορισμός δικαιωμάτων για το %s απέτυχε, γιατί το αντικείμενο δεν βρέθηκε", "Cannot clear expiration date. Shares are required to have an expiration date." : "Δεν είναι σαφής η ημερομηνία λήξης. Ο διαμοιρασμός πρέπει να έχει ημερομηνία λήξης", "Cannot increase permissions of %s" : "Αδυναμία αύξησης των δικαιωμάτων του %s", "Files can't be shared with delete permissions" : "Δεν μπορεί να γίνει διαμοιρασμός αρχείων με δικαιώματα διαγραφής", "Files can't be shared with create permissions" : "Δεν μπορεί να γίνει διαμοιρασμός αρχείων με δικαιώματα δημιουργίας", "Cannot set expiration date more than %s days in the future" : "Δεν είναι δυνατό να τεθεί η ημερομηνία λήξης σε περισσότερες από %s ημέρες στο μέλλον", "Personal" : "Προσωπικά", "Admin" : "Διαχείριση", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Αυτό μπορεί συνήθως να διορθωθεί %sδίνοντας διακαιώματα εγγραφής για τον κατάλογο εφαρμογών στο διακομιστή δικτύου%s ή απενεργοποιώντας το κέντρο εφαρμογών στο αρχείο config.", "Cannot create \"data\" directory (%s)" : "Αδυναμία δημιουργίας του καταλόγου \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Αυτό μπορεί συνήθως να διορθωθεί<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> δίνοντας στον διακομιστή ιστού δικαιώματα εγγραφής στον βασικό κατάλογο</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Τα δικαιώματα πρόσβασης μπορούν συνήθως να διορθωθούν %sδίνοντας δικαιώματα εγγραφής για τον βασικό κατάλογο στο διακομιστή δικτύου%s.", "Data directory (%s) is readable by other users" : "Ο κατάλογος δεδομένων (%s) είναι διαθέσιμος προς ανάγνωση από άλλους χρήστες", "Data directory (%s) must be an absolute path" : "Κατάλογος δεδομένων (%s) πρεπει να είναι απόλυτη η διαδρομή", "Data directory (%s) is invalid" : "Ο κατάλογος δεδομένων (%s) είναι άκυρος", "Please check that the data directory contains a file \".ocdata\" in its root." : "Παρακαλώ ελέγξτε ότι ο κατάλογος δεδομένων περιέχει ένα αρχείο \".ocdata\" στη βάση του." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/cs.json 0000604 00000054740 15247130447 0006633 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Nelze zapisovat do adresáře \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "To lze obvykle vyřešit povolením zápisu webovému serveru do konfiguračního adresáře", "See %s" : "Viz %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "To lze obvykle vyřešit povolením zápisu webovému serveru do konfiguračního adresáře. Viz %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Soubory aplikace %$1s nebyly řádně nahrazeny. Ujistěte se, že je to verze kompatibilní se serverem.", "Sample configuration detected" : "Byla detekována vzorová konfigurace", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Pravděpodobně byla zkopírována konfigurační nastavení ze vzorových souborů. Toto není podporováno a může poškodit vaši instalaci. Nahlédněte prosím do dokumentace před prováděním změn v souboru config.php", "%1$s and %2$s" : "%1$s a %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s a %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s a %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s a %5$s", "Education Edition" : "Edice pro výuku", "Enterprise bundle" : "Enterprise balíček", "Groupware bundle" : "Balíček groupware", "Social sharing bundle" : "Balíček sociálního sdílení", "PHP %s or higher is required." : "Je vyžadováno PHP %s nebo vyšší.", "PHP with a version lower than %s is required." : "Je vyžadováno PHP ve verzi nižší než %s.", "%sbit or higher PHP required." : "Je vyžadováno PHP %sbit nebo vyšší.", "Following databases are supported: %s" : "Jsou podporovány následující databáze: %s", "The command line tool %s could not be found" : "Nástroj příkazového řádku %s nebyl nalezen", "The library %s is not available." : "Knihovna %s není dostupná.", "Library %s with a version higher than %s is required - available version %s." : "Je vyžadována knihovna %s ve verzi vyšší než %s - dostupná verze %s.", "Library %s with a version lower than %s is required - available version %s." : "Je vyžadována knihovna %s ve verzi nižší než %s - dostupná verze %s.", "Following platforms are supported: %s" : "Jsou podporovány následující systémy: %s", "Server version %s or higher is required." : "Je potřeba verze serveru %s nebo vyšší.", "Server version %s or lower is required." : "Je potřeba verze serveru %s nebo nižší.", "Unknown filetype" : "Neznámý typ souboru", "Invalid image" : "Chybný obrázek", "Avatar image is not square" : "Avatar není čtvercový", "today" : "dnes", "yesterday" : "včera", "_%n day ago_::_%n days ago_" : ["včera","před %n dny","před %n dny"], "last month" : "minulý měsíc", "_%n month ago_::_%n months ago_" : ["před %n měsícem","před %n měsíci","před %n měsíci"], "last year" : "minulý rok", "_%n year ago_::_%n years ago_" : ["před rokem","před %n lety","před %n lety"], "_%n hour ago_::_%n hours ago_" : ["před %n hodinou","před %n hodinami","před %n hodinami"], "_%n minute ago_::_%n minutes ago_" : ["před %n minutou","před %n minutami","před %n minutami"], "seconds ago" : "před pár sekundami", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul s ID: %s neexistuje. Povolte ho v nastavení aplikací, nebo kontaktujte vašeho administrátora.", "File name is a reserved word" : "Jméno souboru je rezervované slovo", "File name contains at least one invalid character" : "Jméno souboru obsahuje nejméně jeden neplatný znak", "File name is too long" : "Jméno souboru je moc dlouhé", "Dot files are not allowed" : "Jména souborů začínající tečkou nejsou povolena", "Empty filename is not allowed" : "Prázdné jméno souboru není povoleno", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Aplikace \"%s\" nemůže být nainstalována protože soubor appinfo nelze přečíst.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikaci \"%s\" nelze nainstalovat, protože není kompatibilní s touto verzí serveru.", "This is an automatically sent email, please do not reply." : "Toto je automaticky odesílaný e-mail, prosím, neodpovídejte.", "Help" : "Nápověda", "Apps" : "Aplikace", "Settings" : "Nastavení", "Log out" : "Odhlásit se", "Users" : "Uživatelé", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Základní nastavení", "Sharing" : "Sdílení", "Security" : "Zabezpečení", "Encryption" : "Šifrování", "Additional settings" : "Dodatečná nastavení", "Tips & tricks" : "Tipy a triky", "Personal info" : "Osobní informace", "Sync clients" : "Synchronizační klienti", "Unlimited" : "Neomezeně", "__language_name__" : "Česky", "Verifying" : "Ověření", "Verifying …" : "Ověřování …", "Verify" : "Ověřit", "%s enter the database username and name." : "%s zadejte uživatelské jméno a jméno databáze.", "%s enter the database username." : "Zadejte uživatelské jméno %s databáze.", "%s enter the database name." : "Zadejte název databáze pro %s databáze.", "%s you may not use dots in the database name" : "V názvu databáze %s nesmíte používat tečky.", "Oracle connection could not be established" : "Spojení s Oracle nemohlo být navázáno", "Oracle username and/or password not valid" : "Uživatelské jméno či heslo Oracle není platné", "PostgreSQL username and/or password not valid" : "Uživatelské jméno či heslo PostgreSQL není platné", "You need to enter details of an existing account." : "Musíte zadat údaje existujícího účtu.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X není podporován a %s nebude na této platformě správně fungovat. Používejte pouze na vlastní nebezpečí!", "For the best results, please consider using a GNU/Linux server instead." : "Místo toho zvažte pro nejlepší funkčnost použití GNU/Linux serveru.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Vypadá to, že tato %s instance běží v 32-bitovém PHP prostředí a byl nakonfigurován open_basedir v php.ini. Toto povede k problémům se soubory většími než 4 GB a není doporučováno.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Odstraňte prosím open_basedir nastavení ve svém php.ini nebo přejděte na 64-bitové PHP.", "Set an admin username." : "Zadejte uživatelské jméno správce.", "Set an admin password." : "Zadejte heslo správce.", "Can't create or write into the data directory %s" : "Nelze vytvořit nebo zapisovat do datového adresáře %s", "Invalid Federated Cloud ID" : "Neplatné sdružené cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Sdílení %s selhalo, podpůrná vrstva nepodporuje typ sdílení %i", "Sharing %s failed, because the file does not exist" : "Sdílení %s selhalo, protože soubor neexistuje", "You are not allowed to share %s" : "Nemáte povoleno sdílet %s", "Sharing %s failed, because you can not share with yourself" : "Sdílení %s selhalo, protože nemůžete sdílet sami se sebou", "Sharing %s failed, because the user %s does not exist" : "Sdílení položky %s selhalo, protože uživatel %s neexistuje", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Sdílení položky %s selhalo, protože uživatel %s není členem žádné skupiny společné s uživatelem %s", "Sharing %s failed, because this item is already shared with %s" : "Sdílení položky %s selhalo, protože položka již je s uživatelem %s sdílena", "Sharing %s failed, because this item is already shared with user %s" : "Sdílení položky %s selhalo, protože ta je již s uživatelem %s sdílena", "Sharing %s failed, because the group %s does not exist" : "Sdílení položky %s selhalo, protože skupina %s neexistuje", "Sharing %s failed, because %s is not a member of the group %s" : "Sdílení položky %s selhalo, protože uživatel %s není členem skupiny %s", "You need to provide a password to create a public link, only protected links are allowed" : "Pro vytvoření veřejného odkazu je nutné zadat heslo, jsou povoleny pouze chráněné odkazy", "Sharing %s failed, because sharing with links is not allowed" : "Sdílení položky %s selhalo, protože sdílení pomocí linků není povoleno", "Not allowed to create a federated share with the same user" : "Není povoleno vytvořit propojené sdílení s tím samým uživatelem", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Sdílení %s selhalo, %s se nepodařilo nalézt, server pravděpodobně právě není dostupný.", "Share type %s is not valid for %s" : "Sdílení typu %s není korektní pro %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Nelze nastavit datum vypršení platnosti. Sdílení nemůže vypršet později než za %s po zveřejnění", "Cannot set expiration date. Expiration date is in the past" : "Nelze nastavit datum vypršení platnosti. Datum vypršení je v minulosti", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Úložiště pro sdílení %s musí implementovat rozhraní OCP\\Share_Backend", "Sharing backend %s not found" : "Úložiště sdílení %s nenalezeno", "Sharing backend for %s not found" : "Úložiště sdílení pro %s nenalezeno", "Sharing failed, because the user %s is the original sharer" : "Sdílení položky selhalo, protože uživatel %s je originálním vlastníkem", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Sdílení položky %s selhalo, protože jsou k tomu nutná vyšší oprávnění, než jaká byla %s povolena.", "Sharing %s failed, because resharing is not allowed" : "Sdílení položky %s selhalo, protože znovu-sdílení není povoleno", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Sdílení položky %s selhalo, protože úložiště sdílení %s nenalezla zdroj", "Sharing %s failed, because the file could not be found in the file cache" : "Sdílení položky %s selhalo, protože soubor nebyl nalezen ve vyrovnávací paměti", "Can’t increase permissions of %s" : "Nelze zvýšit oprávnění %s", "Files can’t be shared with delete permissions" : "Soubory nelze sdílet s oprávněními k odstranění", "Files can’t be shared with create permissions" : "Soubory nelze sdílet s oprávněními k vytváření", "Expiration date is in the past" : "Datum vypršení je v minulosti", "Can’t set expiration date more than %s days in the future" : "Nelze nastavit datum vypršení platnosti více než %s dní v budoucnu", "%s shared »%s« with you" : "%s s vámi sdílí »%s«", "%s shared »%s« with you." : "%s s vámi sdílel(a) »%s»", "Click the button below to open it." : "Pro otevření kliknětena tlačítko níže.", "Open »%s«" : "Otevřít »%s«", "%s via %s" : "%s pomocí %s", "The requested share does not exist anymore" : "Požadované sdílení již neexistuje", "Could not find category \"%s\"" : "Nelze nalézt kategorii \"%s\"", "Sunday" : "Neděle", "Monday" : "Pondělí", "Tuesday" : "Úterý", "Wednesday" : "Středa", "Thursday" : "Čtvrtek", "Friday" : "Pátek", "Saturday" : "Sobota", "Sun." : "Ne", "Mon." : "Po", "Tue." : "Út", "Wed." : "St", "Thu." : "Čt", "Fri." : "Pá", "Sat." : "So", "Su" : "Ne", "Mo" : "Po", "Tu" : "Út", "We" : "St", "Th" : "Čt", "Fr" : "Pá", "Sa" : "So", "January" : "Leden", "February" : "Únor", "March" : "Březen", "April" : "Duben", "May" : "Květen", "June" : "Červen", "July" : "Červenec", "August" : "Srpen", "September" : "Září", "October" : "Říjen", "November" : "Listopad", "December" : "Prosinec", "Jan." : "leden", "Feb." : "únor", "Mar." : "březen", "Apr." : "duben", "May." : "květen", "Jun." : "červen", "Jul." : "červenec", "Aug." : "srpen", "Sep." : "září", "Oct." : "říjen", "Nov." : "listopad", "Dec." : "prosinec", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Pouze následující znaky jsou povoleny pro uživatelské jméno: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Musíte zadat platné uživatelské jméno", "Username contains whitespace at the beginning or at the end" : "Uživatelské jméno obsahuje mezery na svém začátku nebo konci", "Username must not consist of dots only" : "Uživatelské jméno se nesmí skládat ze samých teček", "A valid password must be provided" : "Musíte zadat platné heslo", "The username is already being used" : "Uživatelské jméno je již využíváno", "User disabled" : "Uživatel zakázán", "Login canceled by app" : "Přihlášení zrušeno aplikací", "No app name specified" : "Nebyl zadan název aplikace", "App '%s' could not be installed!" : "Aplikaci '%s' nelze nainstalovat!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Aplikaci \"%s\" nelze nainstalovat, protože nejsou splněny následující závislosti: %s", "a safe home for all your data" : "bezpečný domov pro všechna vaše data", "File is currently busy, please try again later" : "Soubor je používán, zkus to později", "Can't read file" : "Nelze přečíst soubor", "Application is not enabled" : "Aplikace není povolena", "Authentication error" : "Chyba ověření", "Token expired. Please reload page." : "Token vypršel. Obnovte prosím stránku.", "Unknown user" : "Neznámý uživatel", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nejsou instalovány ovladače databází (sqlite, mysql nebo postresql).", "Cannot write into \"config\" directory" : "Nelze zapisovat do adresáře \"config\"", "Cannot write into \"apps\" directory" : "Nelze zapisovat do adresáře \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "To lze obvykle vyřešit povolením zápisu webovému serveru do adresáře apps nebo zakázáním appstore v konfiguračním souboru. Viz %s", "Cannot create \"data\" directory" : "Nelze vytvořit datový adresář", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "To lze obvykle vyřešit povolením zápisu webovému serveru do kořenového adresáře. Viz %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Oprávnění lze obvykle napravit povolením zápisu webovému serveru do kořenového adresáře. Viz %s.", "Setting locale to %s failed" : "Nastavení jazyka na %s selhalo", "Please install one of these locales on your system and restart your webserver." : "Prosím nainstalujte alespoň jeden z těchto jazyků do svého systému a restartujte webový server.", "Please ask your server administrator to install the module." : "Požádejte svého správce systému o instalaci tohoto modulu.", "PHP module %s not installed." : "PHP modul %s není nainstalován.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP hodnota \"%s\" není nastavena na \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Změna tohoto nastavení v php.ini umožní Nextcloudu opět běžet", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload je nastaven na \"%s\" místo očekávané hodnoty \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Pro nápravu nastavte <code>mbstring.func_overload</code> na <code>0</code> v souboru php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Je požadováno minimálně libxml2 2.7.0. Aktuálně je nainstalována verze %s.", "To fix this issue update your libxml2 version and restart your web server." : "Pro opravu tohoto problému aktualizujte libxml2 a restartujte web server.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP je patrně nastaveno tak, aby odstraňovalo bloky komentářů. Toto bude mít za následek znepřístupnění mnoha důležitých aplikací.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Toto je pravděpodobně způsobeno aplikacemi pro urychlení načítání jako jsou Zend OPcache nebo eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP moduly jsou nainstalovány, ale stále se tváří jako chybějící?", "Please ask your server administrator to restart the web server." : "Požádejte svého správce systému o restart webového serveru.", "PostgreSQL >= 9 required" : "Je vyžadováno PostgreSQL >= 9", "Please upgrade your database version" : "Aktualizujte prosím verzi své databáze", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Změňte prosím práva na 0770, aby adresář nemohl být otevřen ostatními uživateli.", "Your data directory is readable by other users" : "Váš datový adresář mohou číst ostatní užovatelé", "Your data directory must be an absolute path" : "Váš datový adresář musí být absolutní cesta", "Check the value of \"datadirectory\" in your configuration" : "Ověřte hodnotu \"datadirectory\" ve své konfiguraci", "Your data directory is invalid" : "Váš datový adresář je neplatný", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Ujistěte se, že v kořenovém adresáři je soubor s názvem \".ocdata\".", "Could not obtain lock type %d on \"%s\"." : "Nelze získat zámek typu %d na \"%s\".", "Storage unauthorized. %s" : "Úložiště neověřeno. %s", "Storage incomplete configuration. %s" : "Nekompletní konfigurace úložiště. %s", "Storage connection error. %s" : "Chyba připojení úložiště. %s", "Storage is temporarily not available" : "Úložiště je dočasně nedostupné", "Storage connection timeout. %s" : "Vypršení připojení k úložišti. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "To lze obvykle vyřešit %spovolením zápisu webovému serveru do konfiguračního adresáře%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul s id: %s neexistuje. Povolte ho prosím ve svých nastaveních aplikací nebo kontaktujte svého administrátora.", "Server settings" : "Nastavení serveru", "DB Error: \"%s\"" : "Chyba databáze: \"%s\"", "Offending command was: \"%s\"" : "Příslušný příkaz byl: \"%s\"", "You need to enter either an existing account or the administrator." : "Musíte zadat existující účet či správce.", "Offending command was: \"%s\", name: %s, password: %s" : "Příslušný příkaz byl: \"%s\", jméno: %s, heslo: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Nastavení oprávnění pro %s selhalo, protože jsou k tomu nutná vyšší oprávnění, než jaká byla povolena pro %s", "Setting permissions for %s failed, because the item was not found" : "Nastavení práv pro %s selhalo, protože položka nebyla nalezena", "Cannot clear expiration date. Shares are required to have an expiration date." : "Nelze smazat datum vypršení platnosti. Sdílená data vyžadují datum vypršení platnosti odkazu.", "Cannot increase permissions of %s" : "Nelze navýšit oprávnění u %s", "Files can't be shared with delete permissions" : "Soubory nelze sdílet s oprávněními ke smazání", "Files can't be shared with create permissions" : "Soubory nelze sdílet s vytvořenými oprávněními", "Cannot set expiration date more than %s days in the future" : "Datum vypršení nelze nastavit na více než %s dní do budoucnosti", "Personal" : "Osobní", "Admin" : "Administrace", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "To lze obvykle vyřešit %spovolením zápisu webovému serveru do adresáře apps%s nebo zakázáním appstore v konfiguračním souboru.", "Cannot create \"data\" directory (%s)" : "Nelze vytvořit adresář \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Toto může být obvykle opraveno <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">nastavením přístupových práv webového serveru pro zápis do kořenového adresáře</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Oprávnění lze obvykle napravit %spovolením zápisu webovému serveru do kořenového adresáře%s.", "Data directory (%s) is readable by other users" : "Datový adresář (%s) je čitelný i ostatními uživateli", "Data directory (%s) must be an absolute path" : "Cesta k datovému adresáři (%s) musí být uvedena absolutně", "Data directory (%s) is invalid" : "Datový adresář (%s) je neplatný", "Please check that the data directory contains a file \".ocdata\" in its root." : "Ověřte prosím, že kořenový adresář s daty obsahuje soubor \".ocdata\"." },"pluralForm" :"nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" } l10n/ast.json 0000604 00000045343 15247130447 0007014 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "¡Nun pue escribise nel direutoriu «config»!", "This can usually be fixed by giving the webserver write access to the config directory" : "Davezu esto pue iguase dándo-y al sirvidor web accesu d'escritura al direutoriu de configuración", "See %s" : "Mira %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Esto davezu íguase dando'l permisu d'escritura nel direutoriu de configuración al sirvidor web. Mira %s", "Sample configuration detected" : "Configuración d'amuesa detectada", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Detectose que la configuración d'amuesa copiose. Esto pue encaboxar la instalación y dexala ensín soporte. Llee la documentación enantes de facer cambéos en config.php", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "Education Edition" : "Edición educativa", "Enterprise bundle" : "Llote empresarial", "Social sharing bundle" : "Llote de compartición social", "PHP %s or higher is required." : "Necesítase PHP %s o superior", "PHP with a version lower than %s is required." : "Necesítase una versión PHP anterior a %s", "%sbit or higher PHP required." : "Necesítase PHP %sbit o superior", "Following databases are supported: %s" : "Les siguientes bases de datos tan sofitaes: %s", "The command line tool %s could not be found" : "La ferramienta línea de comandu %s nun pudo alcontrase", "The library %s is not available." : "La librería %s nun ta disponible", "Library %s with a version higher than %s is required - available version %s." : "Necesítase una librería %s con ua versión superior a %s - versión disponible %s.", "Library %s with a version lower than %s is required - available version %s." : "Necesítase una librería %s con una versión anterior a %s - versión disponible %s.", "Following platforms are supported: %s" : "Les siguientes plataformes tan sofitaes: %s", "Unknown filetype" : "Triba de ficheru desconocida", "Invalid image" : "Imaxe inválida", "Avatar image is not square" : "La imaxe del avatar nun ye cuadrada", "today" : "güei", "yesterday" : "ayeri", "_%n day ago_::_%n days ago_" : ["hai %n día","hai %n díes"], "last month" : "mes caberu", "_%n month ago_::_%n months ago_" : ["hai %n mes","hai %n meses"], "last year" : "añu caberu", "_%n year ago_::_%n years ago_" : ["hai %n añu","hai %n años"], "_%n hour ago_::_%n hours ago_" : ["hai %n hora","hai %n hores"], "_%n minute ago_::_%n minutes ago_" : ["hai %n minutu","hai %n minutos"], "seconds ago" : "hai segundos", "File name is a reserved word" : "El nome de ficheru ye una pallabra reservada", "File name contains at least one invalid character" : "El nome del ficheru contién polo menos un carácter non válidu", "File name is too long" : "El nome de ficheru ye demasiáu llargu", "Empty filename is not allowed" : "Nun s'almite un nome de ficheru baleru", "App \"%s\" cannot be installed because appinfo file cannot be read." : "L'aplicación \"%s\" nun puede instalase porque nun se llee'l ficheru appinfo.", "Help" : "Ayuda", "Apps" : "Aplicaciones", "Log out" : "Zarrar sesión", "Users" : "Usuarios", "APCu" : "APCu", "Basic settings" : "Axustes básicos", "Security" : "Seguranza", "Encryption" : "Cifráu", "Additional settings" : "Axustes adicionales", "Tips & tricks" : "Conseyos y trucos", "__language_name__" : "Asturianu", "%s enter the database username and name." : "%s introducir el nome d'usuariu y el nome de la base de datos .", "%s enter the database username." : "%s introducir l'usuariu de la base de datos.", "%s enter the database name." : "%s introducir nome de la base de datos.", "%s you may not use dots in the database name" : "%s nun pues usar puntos nel nome de la base de datos", "Oracle connection could not be established" : "Nun pudo afitase la conexón d'Oracle", "Oracle username and/or password not valid" : "Nome d'usuariu o contraseña d'Oracle non válidos", "PostgreSQL username and/or password not valid" : "Nome d'usuariu o contraseña PostgreSQL non válidos", "You need to enter details of an existing account." : "Precises introducir los detalles d'una cuenta esistente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nun ta sofitáu y %s nun furrulará afayadizamente nesta plataforma. ¡Úsalu baxo'l to riesgu!", "For the best results, please consider using a GNU/Linux server instead." : "Pa los meyores resultaos, por favor considera l'usu d'un sirvidor GNU/Linux nel so llugar.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Paez ser que la instancia %s ta executándose nun entornu de PHP 32 bits y el open_basedir configuróse en php.ini. Esto va dar llugar a problemes colos ficheros de más de 4 GB y nun ye nada recomendable.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor, desanicia la configuración open_basedir dientro la so php.ini o camude a PHP 64 bits.", "Set an admin username." : "Afitar nome d'usuariu p'almin", "Set an admin password." : "Afitar contraseña p'almin", "Can't create or write into the data directory %s" : "Nun pue crease o escribir dientro los datos del direutoriu %s", "Invalid Federated Cloud ID" : "ID non válida de ñube federada", "Sharing %s failed, because the backend does not allow shares from type %i" : "Compartir %s falló, por cuenta qu'el backend nun dexa acciones de tipu %i", "Sharing %s failed, because the file does not exist" : "Compartir %s falló, porque'l ficheru nun esiste", "You are not allowed to share %s" : "Nun tienes permisu pa compartir %s", "Sharing %s failed, because you can not share with yourself" : "Compartir %s falló, porque nun puede compartise contigo mesmu", "Sharing %s failed, because the user %s does not exist" : "Compartir %s falló, yá que l'usuariu %s nun esiste", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Compartir %s falló, yá que l'usuariu %s nun ye miembru de nengún de los grupos de los que ye miembru %s", "Sharing %s failed, because this item is already shared with %s" : "Compartir %s falló, porque esti elementu yá ta compartiéndose con %s", "Sharing %s failed, because this item is already shared with user %s" : "Compartir %s falló, porque esti elementu yá ta compartiéndose col usuariu %s", "Sharing %s failed, because the group %s does not exist" : "Compartir %s falló, porque'l grupu %s nun esiste", "Sharing %s failed, because %s is not a member of the group %s" : "Compartir %s falló, porque %s nun ye miembru del grupu %s", "You need to provide a password to create a public link, only protected links are allowed" : "Necesites apurrir una contraseña pa crear un enllaz públicu, namái tan permitíos los enllaces protexíos", "Sharing %s failed, because sharing with links is not allowed" : "Compartir %s falló, porque nun se permite compartir con enllaces", "Not allowed to create a federated share with the same user" : "Nun s'almite crear un recursu compartíu federáu col mesmu usuariu", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Compartir %s falló, nun pudo atopase %s, pue qu'el servidor nun seya anguaño algamable.", "Share type %s is not valid for %s" : "La triba de compartición %s nun ye válida pa %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Nun pue afitase la data de caducidá. Ficheros compartíos nun puen caducar dempués de %s de compartise", "Cannot set expiration date. Expiration date is in the past" : "Nun pue afitase la data d'espiración. La data d'espiración ta nel pasáu", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El motor compartíu %s tien d'implementar la interfaz OCP\\Share_Backend", "Sharing backend %s not found" : "Nun s'alcontró'l botón de compartición %s", "Sharing backend for %s not found" : "Nun s'alcontró'l botón de partición pa %s", "Sharing failed, because the user %s is the original sharer" : "Compartir falló, porque l'usuariu %s ye'l compartidor orixinal", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Compartir %s falló, porque los permisos perpasen los otorgaos a %s", "Sharing %s failed, because resharing is not allowed" : "Compartir %s falló, porque nun se permite la re-compartición", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Compartir %s falló porque'l motor compartíu pa %s podría nun atopar el so orixe", "Sharing %s failed, because the file could not be found in the file cache" : "Compartir %s falló, yá que'l ficheru nun pudo atopase na caché de ficheru", "Can’t increase permissions of %s" : "Nun pue aumentase los permisos de %s", "Files can’t be shared with delete permissions" : "Los ficheros nun puen compartise colos permisos de desaniciu", "Files can’t be shared with create permissions" : "Los ficheros nun puen compartise colos permisos de creación", "Expiration date is in the past" : "La data de caducidá ta nel pasáu.", "Can’t set expiration date more than %s days in the future" : "Nun pue afitase la data de caducidá más de %s díes nel futuru", "%s shared »%s« with you" : "%s compartió »%s« contigo", "%s via %s" : "%s via %s", "Could not find category \"%s\"" : "Nun pudo alcontrase la estaya \"%s.\"", "Sunday" : "Domingu", "Monday" : "Llunes", "Friday" : "Vienres", "Saturday" : "Sábadu", "Mon." : "Llu.", "Sat." : "Sáb.", "January" : "Xineru", "February" : "Febreru", "March" : "Marzu", "April" : "Abril", "May" : "Mayu", "June" : "Xunu", "July" : "Xunetu", "August" : "Agostu", "September" : "Setiembre", "October" : "Ochobre", "November" : "Payares", "December" : "Avientu", "Jan." : "Xin.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "May.", "Jun." : "Xun.", "Jul." : "Xnt.", "Sep." : "Set.", "Oct." : "Och.", "Nov." : "Pay.", "Dec." : "Avi.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Namái tan permitíos los siguientes caráuteres nun nome d'usuariu: \"a-z\", \"A-Z\", \"0-9\", y \"_.@-'\"", "A valid username must be provided" : "Tien d'apurrise un nome d'usuariu válidu", "Username contains whitespace at the beginning or at the end" : "El nome d'usuario contién espacios en blancu al entamu o al final", "Username must not consist of dots only" : "El nome d'usuariu nun pue tener puntos", "A valid password must be provided" : "Tien d'apurrise una contraseña válida", "The username is already being used" : "El nome d'usuariu yá ta usándose", "User disabled" : "Usuariu desactiváu", "Login canceled by app" : "Aniciar sesión canceláu pola aplicación", "No app name specified" : "Nun s'especificó nome de l'aplicación", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "L'aplicación \"%s\" nun puede instalase porque les siguientes dependencies nun se cumplen: %s", "a safe home for all your data" : "un llar seguru pa tolos tos datos", "File is currently busy, please try again later" : "Fichaeru ta ocupáu, por favor intentelo de nuevu más tarde", "Can't read file" : "Nun ye a lleese'l ficheru", "Application is not enabled" : "L'aplicación nun ta habilitada", "Authentication error" : "Fallu d'autenticación", "Token expired. Please reload page." : "Token caducáu. Recarga la páxina.", "Unknown user" : "Usuariu desconocíu", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nun hai controladores de bases de datos (sqlite, mysql, o postgresql)", "Cannot write into \"config\" directory" : "Nun pue escribise nel direutoriu \"config\"", "Cannot write into \"apps\" directory" : "Nun pue escribise nel direutoriu \"apps\"", "Setting locale to %s failed" : "Falló l'activación del idioma %s", "Please install one of these locales on your system and restart your webserver." : "Instala ún d'estos locales nel to sistema y reanicia'l sirvidor web", "Please ask your server administrator to install the module." : "Por favor, entrúga-y al to alministrador del sirvidor pa instalar el módulu.", "PHP module %s not installed." : "Nun ta instaláu'l módulu PHP %s", "PHP setting \"%s\" is not set to \"%s\"." : "La configuración de PHP \"%s\" nun s'afita \"%s\".", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload afita \"%s\" en llugar del valor esperáu \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Pa solucionar esti problema definíu <code>mbstring.func_overload</code>a <code>0</code> nel so php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 ríquese siquier. Anguaño ta instaláu %s.", "To fix this issue update your libxml2 version and restart your web server." : "Pa solucionar esti problema actualiza latso versión de libxml2 y reanicia'l to sirvidor web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ta aparentemente configuráu pa desaniciar bloques de documentos en llinia. Esto va facer que delles aplicaciones principales nun tean accesibles.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dablemente esto seya culpa d'un caché o acelerador, como por exemplu Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Instaláronse los módulos PHP, ¿pero tán entá llistaos como faltantes?", "Please ask your server administrator to restart the web server." : "Por favor, entruga al to alministrador pa reaniciar el sirvidor web.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 requeríu", "Please upgrade your database version" : "Por favor, anueva la versión de la to base de datos", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor, camuda los permisos a 0770 pa que'l direutoriu nun pueda llistase por otros usuarios.", "Check the value of \"datadirectory\" in your configuration" : "Comprobar el valor del \"datadirectory\" na so configuración", "Your data directory is invalid" : "El to direutoriu de datos nun ye válidu", "Could not obtain lock type %d on \"%s\"." : "Nun pudo facese'l bloquéu %d en \"%s\".", "Storage unauthorized. %s" : "Almacenamientu desautorizáu. %s", "Storage incomplete configuration. %s" : "Configuración d'almacenamientu incompleta. %s", "Storage connection error. %s" : "Fallu de conexón al almacenamientu. %s", "Storage is temporarily not available" : "L'almacenamientu ta temporalmente non disponible", "Storage connection timeout. %s" : "Tiempu escosao de conexón al almacenamientu. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Davezu esto pue iguase %sdándo-y al sirvidor web accesu d'escritura al direutoriu de configuración%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Nun esiste'l módulu con id: %s . Por favor, activalu na configuración d'aplicaciones o contauta col alministrador.", "Server settings" : "Axustes del sirvidor", "DB Error: \"%s\"" : "Fallu BD: \"%s\"", "Offending command was: \"%s\"" : "Comandu infractor: \"%s\"", "You need to enter either an existing account or the administrator." : "Tienes d'inxertar una cuenta esistente o la del alministrador.", "Offending command was: \"%s\", name: %s, password: %s" : "El comandu infractor foi: \"%s\", nome: %s, contraseña: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Falló dar permisos a %s, porque los permisos son mayores que los otorgaos a %s", "Setting permissions for %s failed, because the item was not found" : "Falló dar permisos a %s, porque l'elementu nun s'atopó", "Cannot clear expiration date. Shares are required to have an expiration date." : "Non puede desaniciar la fecha de caducidá. Compartir obliga a tener una fecha de caducidá.", "Cannot increase permissions of %s" : "Nun se pueden aumentar los permisos de %s", "Files can't be shared with delete permissions" : "Los ficheros nun pueden compartise con permisos desaniciaos", "Files can't be shared with create permissions" : "Los ficheros nun pueden compartise con crear permisos", "Cannot set expiration date more than %s days in the future" : "Nun pue afitase la data d'espiración más que %s díes nel futuru", "Personal" : "Personal", "Admin" : "Almin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Esto pue iguase %sdando permisos d'escritura al sirvidor Web nel direutoriu%s d'apps o deshabilitando la tienda d'apps nel ficheru de configuración.", "Cannot create \"data\" directory (%s)" : "Nun pue crease'l direutoriu \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Esto pue iguase davezu <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">dándo-y accesu d'escritura al direutoriu raigañu</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Davezu los permisos puen iguase %sdándo-y al sirvidor web accesu d'escritura al direutoriu raigañu%s.", "Data directory (%s) is readable by other users" : "El direutoriu de datos (%s) ye llexible por otros usuarios", "Data directory (%s) must be an absolute path" : "El directoriu de datos (%s) ha de ser una ruta absoluta", "Data directory (%s) is invalid" : "Ye inválidu'l direutoriu de datos (%s)", "Please check that the data directory contains a file \".ocdata\" in its root." : "Verifica que'l direutoriu de datos contién un ficheru \".ocdata\" nel direutoriu raigañu." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/lt_LT.json 0000604 00000036664 15247130447 0007251 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Nepavyksta rašyti į \"config\" katalogą!", "This can usually be fixed by giving the webserver write access to the config directory" : "Tai, dažniausiai, gali būti ištaisyta suteikiant saityno serveriui rašymo prieigą prie konfigūracijos katalogo", "See %s" : "Žiūrėkite %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Tai, dažniausiai, gali būti pataisyta, suteikiant saityno serveriui rašymo prieigą prie konfigūracijos katalogo. Žiūrėkite %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Programėlės %$1s failai buvo pakeisti neteisingai. Įsitikinkite, kad versija yra suderinama su serveriu.", "Sample configuration detected" : "Aptiktas konfigūracijos pavyzdys", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Pastebėta, kad nukopijuota pavyzdinė konfigūracija. Tai gali pažeisti jūsų diegimą ir yra nepalaikoma. Prieš atliekant pakeitimus config.php faile, prašome perskaityti dokumentaciją.", "%1$s and %2$s" : "%1$s ir %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s ir %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s ir %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s ir %5$s", "PHP %s or higher is required." : "Reikalinga PHP %s arba aukštesnė.", "PHP with a version lower than %s is required." : "Reikalinga žemesnė nei %s PHP versija. ", "Following databases are supported: %s" : "Yra palaikomos šios duomenų bazės: %s", "The command line tool %s could not be found" : "Nepavyko rasti komandų eilutės įrankio %s", "The library %s is not available." : "Biblioteka %s nėra prieinama.", "Library %s with a version higher than %s is required - available version %s." : "Bibliotekos %s versija turi būti aukštesnė nei %s - turima versija %s.", "Library %s with a version lower than %s is required - available version %s." : "Bibliotekos %s versija turi būti žemesnė nei %s - turima versija %s.", "Following platforms are supported: %s" : "Yra palaikomos šios platformos: %s", "Server version %s or higher is required." : "Reikalinga %s arba aukštesnė serverio versija ", "Server version %s or lower is required." : "Reikalinga %s arba žemesnė serverio versija. ", "Unknown filetype" : "Nežinomas failo tipas", "Invalid image" : "Neteisingas paveikslas", "today" : "šiandien", "yesterday" : "vakar", "_%n day ago_::_%n days ago_" : ["prieš %n dieną","prieš %n dienas","prieš %n dienų"], "last month" : "praeitą mėnesį", "_%n month ago_::_%n months ago_" : ["prieš %n mėnesį","prieš %n mėnesius","prieš %n mėnesių"], "last year" : "praeitais metais", "_%n year ago_::_%n years ago_" : ["prieš %n metus","prieš %n metus","prieš %n metų"], "_%n hour ago_::_%n hours ago_" : ["prieš %n valandą","prieš %n valandas","prieš %n valandų"], "_%n minute ago_::_%n minutes ago_" : ["prieš %n minutę","prieš % minutes","prieš %n minučių"], "seconds ago" : "prieš keletą sekundžių", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulio, kurio id: %s, nėra. Prašome jį įjungti savo programėlių nustatymuose arba susisiekti su savo administratoriumi.", "File name is a reserved word" : "Failo pavadinimas negalimas, žodis rezervuotas", "File name contains at least one invalid character" : "Failo vardas sudarytas iš neleistinų simbolių", "File name is too long" : "Failo pavadinimas per ilgas", "Empty filename is not allowed" : "Tuščias failo pavadinimas nėra leidžiamas", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Programėlė \"%s\" negali būti įdiegta, kadangi negalima perskaityti appinfo failo.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Programėlė \"%s\" negali būti įdiegta, kadangi ji nėra suderinama su serverio versija.", "This is an automatically sent email, please do not reply." : "Tai yra automatinis pranešimas, prašome neatsakyti.", "Help" : "Pagalba", "Apps" : "Programėlės", "Settings" : "Nustatymai", "Log out" : "Atsijungti", "Users" : "Naudotojai", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Pagrindiniai nustatymai", "Sharing" : "Dalijimasis", "Security" : "Saugumas", "Encryption" : "Šifravimas", "Additional settings" : "Papildomi nustatymai", "Tips & tricks" : "Patarimai ir gudrybės", "Personal info" : "Asmeninė informacija", "Sync clients" : "Sinchronizavimo klientas", "Unlimited" : "Neribota", "__language_name__" : "Lietuvių", "Verifying" : "Tikrinimas", "Verifying …" : "Tikrinama...", "Verify" : "Patikrinti", "%s enter the database username and name." : "%s įrašykite duomenų bazės naudotojo vardą ir pavadinimą.", "%s enter the database username." : "%s įrašykite duomenų bazės naudotojo vardą.", "%s enter the database name." : "%s įrašykite duomenų bazės pavadinimą.", "%s you may not use dots in the database name" : "%s negalite naudoti taškų duombazės pavadinime", "Oracle connection could not be established" : "Nepavyko užmegzti Oracle ryšio", "Oracle username and/or password not valid" : "Neteisingas Oracle naudotojo vardas ir/arba slaptažodis", "PostgreSQL username and/or password not valid" : "Neteisingas PostgreSQL naudotojo vardas ir/arba slaptažodis", "You need to enter details of an existing account." : "Jūs turite suvesti egzistuojančios paskyros duomenis.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nėra palaikomas, %s neveiks tinkamai šioje platformoje. Naudodami prisiimate visą riziką !", "Set an admin username." : "Nustatyti administratoriaus naudotojo vardą.", "Set an admin password." : "Nustatyti administratoriaus slaptažodį.", "Can't create or write into the data directory %s" : "Negalima nuskaityti arba rašyti į duomenų katalogą. %s", "Invalid Federated Cloud ID" : "Netinkamas Centralizuoto Serverio ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "%s dalinimasis nepavyko, nes sistema nepalaiko šio duomenų tipo %i", "Sharing %s failed, because the file does not exist" : "%s dalinimasis nepavyko, nes failas neegzistuoja. ", "You are not allowed to share %s" : "Jums neleidžiama bendrinti %s", "Sharing %s failed, because you can not share with yourself" : "%s bendrinimas nepavyko, jūs negalite bendrinti su savimi pačiu.", "Sharing %s failed, because the user %s does not exist" : "%s bendrinimas nepavyko, nes naudotojas %s neegzistuoja", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s bendrinimas nepavyko, nes naudotojas %s nėra tos pačios grupės, kaip %s, narys.", "Sharing %s failed, because this item is already shared with %s" : "%s bendrinimas nepavyko, kadangi šis elementas jau yra bendrinamas su %s", "Sharing %s failed, because this item is already shared with user %s" : "%s bendrinimas nepavyko, kadangi šis elementas jau yra bendrinamas su naudotoju %s", "Sharing %s failed, because the group %s does not exist" : "%s bendrinimas nepavyko, nes grupė %s neegzistuoja", "Sharing %s failed, because %s is not a member of the group %s" : " %s bendrinimas nepavyko, nes %s nėra %s grupės narys.", "You need to provide a password to create a public link, only protected links are allowed" : "Viešoms nuorodoms būtinas slaptažodis, leidžiamos tik apsaugotos nuorodos.", "Sharing %s failed, because sharing with links is not allowed" : "Bendrinimas %s nepavyko, kadangi bendrinimas su nuorodomis yra neleidžiamas.", "Not allowed to create a federated share with the same user" : "Negalima dalintis su identišku naudotoju kitame serveryje", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s pasidalinimas nepavyko, neįmanoma rasti %s, tikėtina, kad serveris šiuo metu nepasiekiamas", "Share type %s is not valid for %s" : "Bendrinimo tipas %s netinka %s", "Cannot set expiration date. Expiration date is in the past" : "Nepavyko nustatyti galiojimo datos. Galiojimo data yra praėjęs laikas.", "Sharing failed, because the user %s is the original sharer" : "Bendrinimas nepavyko, nes naudotojas %s yra bendrintojas.", "Sharing %s failed, because resharing is not allowed" : "%s bendrinimas nepavyko, nes perskirstymas yra neleidžiamas.", "Can’t increase permissions of %s" : "Negalima pridėti papildomų %s leidimų", "Expiration date is in the past" : "Bendrinimo pabaigos data yra praėjęs laikas", "Can’t set expiration date more than %s days in the future" : "Negalima nustatyti galiojimo laiko ilgesnio nei %s dienos.", "%s shared »%s« with you" : "%s pasidalino »%s« su jumis", "%s shared »%s« with you." : "%s pasidalino »%s« su Jumis.", "Click the button below to open it." : "Norėdami atverti failą, spustelėkite mygtuką žemiau.", "Open »%s«" : "Atverti \"%s\"", "%s via %s" : "%s per %s", "The requested share does not exist anymore" : "Pageidaujamas bendrinimas daugiau neegzistuoja.", "Could not find category \"%s\"" : "Nepavyko rasti kategorijos „%s“", "Sunday" : "Sekmadienis", "Monday" : "Pirmadienis", "Tuesday" : "Antradienis", "Wednesday" : "Trečiadienis", "Thursday" : "Ketvirtadienis", "Friday" : "Penktadienis", "Saturday" : "Šeštadienis", "Sun." : "Sek.", "Mon." : "Pir.", "Tue." : "Ant.", "Wed." : "Tre.", "Thu." : "Ket.", "Fri." : "Pen.", "Sat." : "Šeš.", "Su" : "Sk", "Mo" : "Pr", "Tu" : "An", "We" : "Tr", "Th" : "Kt", "Fr" : "Pn", "Sa" : "Št", "January" : "Sausis", "February" : "Vasaris", "March" : "Kovas", "April" : "Balandis", "May" : "Gegužė", "June" : "Birželis", "July" : "Liepa", "August" : "Rugpjūtis", "September" : "Rugsėjis", "October" : "Spalis", "November" : "Lapkritis", "December" : "Gruodis", "Jan." : "Sau.", "Feb." : "Vas.", "Mar." : "Kov.", "Apr." : "Bal.", "May." : "Geg.", "Jun." : "Bir.", "Jul." : "Lie.", "Aug." : "Rgp.", "Sep." : "Rgs.", "Oct." : "Spl.", "Nov." : "Lap.", "Dec." : "Grd.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Naudotojo varde galima naudoti tik sekančius simbolius: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Privalo būti pateiktas tinkamas naudotojo vardas", "Username contains whitespace at the beginning or at the end" : "Naudotojo varde pradžioje ar pabaigoje yra tarpas", "Username must not consist of dots only" : "Naudotojo vardas negali būti sudarytas tik iš taškų.", "A valid password must be provided" : "Slaptažodis turi būti tinkamas", "The username is already being used" : "Naudotojo vardas jau yra naudojamas", "Could not create user" : "Nepavyko sukurti naudotojo", "User disabled" : "Naudotojas išjungtas", "Login canceled by app" : "Programėlė nutraukė prisijungimo procesą", "No app name specified" : "Nenurodytas programėlės pavadinimas", "App '%s' could not be installed!" : "Nepavyko įdiegti '%s' programėlės!", "a safe home for all your data" : "saugūs namai visiems jūsų duomenims", "File is currently busy, please try again later" : "Failas šiuo metu yra užimtas, prašome vėliau pabandyti dar kartą", "Can't read file" : "Nepavyksta perskaityti failo", "Application is not enabled" : "Programa neįjungta", "Authentication error" : "Tapatybės nustatymo klaida", "Token expired. Please reload page." : "Pasibaigė prieigos rakto galiojimas. Prašome įkelti puslapį iš naujo.", "Unknown user" : "Nežinomas naudotojas", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nėra įdiegtos duomenų bazių tvarkyklės (sqlite, mysql, or postgresql)", "Cannot write into \"config\" directory" : "Nepavyksta rašyti į \"config\" katalogą!", "Cannot write into \"apps\" directory" : "Nepavyksta įrašyti į \"apps\" katalogą", "Cannot create \"data\" directory" : "Nepavyksta sukurti katalogo \"data\"", "Please install one of these locales on your system and restart your webserver." : "Prašome įdiekite vieną šių lokalių savo sistemoje ir perkraukite žiniatinklio serverį.", "Please ask your server administrator to install the module." : "Kreipkitės į savo sistemos administratorių, kad jis įdiegtų modulį.", "PHP module %s not installed." : "PHP modulis %s neįdiegtas.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP nustatymas \"%s\" nenustatytas į \"%s\".", "To fix this issue update your libxml2 version and restart your web server." : "Atnaujinkite libxml2 versiją ir perkraukite žiniatinklio serverį, kad sutvarkytumėte šią problemą.", "PHP modules have been installed, but they are still listed as missing?" : "PHP moduliai yra įdiegti, bet jų vis tiek trūksta?", "Please ask your server administrator to restart the web server." : "Kreipkitės į savo sistemos administratorių, kad jis perkrautų žiniatinklio serverį.", "PostgreSQL >= 9 required" : "Reikalinga PostgreSQL >= 9", "Please upgrade your database version" : "Atnaujinkite duomenų bazės versiją.", "Your data directory is invalid" : "Neteisingas duomenų katalogas", "Storage unauthorized. %s" : "Saugykla nesankcionuota. %s", "Storage incomplete configuration. %s" : "Nepilna saugyklos konfigūracija. %s", "Storage connection error. %s" : "Saugyklos sujungimo ryšio klaida. %s", "Storage is temporarily not available" : "Saugykla yra laikinai neprieinama", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulio, kurio id: %s, nėra. Prašome jį įjungti savo programėlių nustatymuose arba susisiekti su savo administratoriumi.", "Server settings" : "Serverio nustatymai", "DB Error: \"%s\"" : "DB klaida: \"%s\"", "Offending command was: \"%s\"" : "Vykdyta komanda buvo: \"%s\"", "You need to enter either an existing account or the administrator." : "Turite prisijungti su egzistuojančia paskyra arba su administratoriumi.", "Offending command was: \"%s\", name: %s, password: %s" : "Vykdyta komanda buvo: \"%s\", name: %s, password: %s", "Cannot increase permissions of %s" : "Negalima pridėti papildomų %s leidimų", "Files can't be shared with delete permissions" : "Failai negali būti bendrinami su trynimo leidimu.", "Files can't be shared with create permissions" : "Failai negali būti bendrinami su sukūrimo leidimu.", "Personal" : "Asmeniniai", "Admin" : "Administravimas", "Cannot create \"data\" directory (%s)" : "Nepavyksta sukurti katalogo \"data\" (%s)", "Data directory (%s) is readable by other users" : "Duomenų katalogą (%s) skaito kiti naudotojai", "Data directory (%s) is invalid" : "Duomenų katalogas (%s) netinkamas." },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" } l10n/tr.json 0000604 00000054150 15247130447 0006646 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "\"config\" klasörüne yazılamadı!", "This can usually be fixed by giving the webserver write access to the config directory" : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir", "See %s" : "Şuraya bakın: %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir. %s bölümüne bakın", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "%1$s uygulamasının dosyaları doğru şekilde değiştirilmedi. Sunucu ile uyumlu dosyaların yüklü olduğundan emin olun.", "Sample configuration detected" : "Örnek yapılandırma algılandı", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Örnek yapılandırmanın kopyalanmış olabileceği tespit edildi. Bu durum kurulumunuzu bozabilir ve desteklenmez. Lütfen config.php dosyasında değişiklik yapmadan önce belgeleri okuyun", "%1$s and %2$s" : "%1$s ve %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s ve %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s ve %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s ve %5$s", "Education Edition" : "Eğitim Sürümü", "Enterprise bundle" : "Kurumsal paket", "Groupware bundle" : "Grup paketi", "Social sharing bundle" : "Sosyal ağ paketi", "PHP %s or higher is required." : "PHP %s ya da daha sonraki bir sürümü gerekli.", "PHP with a version lower than %s is required." : "PHP %s ya da daha önceki bir sürümü gerekli.", "%sbit or higher PHP required." : "%sbit ya da daha sonraki bir PHP sürümü gerekli.", "Following databases are supported: %s" : "Şu veritabanları destekleniyor: %s", "The command line tool %s could not be found" : "%s komut satırı aracı bulunamadı", "The library %s is not available." : "%s kitaplığı bulunamadı.", "Library %s with a version higher than %s is required - available version %s." : "%s kitaplığının %s sonrası bir sürümü gerekli. Geçerli sürüm: %s.", "Library %s with a version lower than %s is required - available version %s." : "%s kitaplığının %s öncesi bir sürümü gerekli. Geçerli sürüm: %s.", "Following platforms are supported: %s" : "Şu platformlar destekleniyor: %s", "Server version %s or higher is required." : "Sunucu %s ya da daha sonraki bir sürüm olmalıdır.", "Server version %s or lower is required." : "Sunucu %s ya da daha önceki bir sürüm olmalıdır.", "Unknown filetype" : "Dosya türü bilinmiyor", "Invalid image" : "Görsel geçersiz", "Avatar image is not square" : "Avatar görseli kare değil", "today" : "bugün", "yesterday" : "dün", "_%n day ago_::_%n days ago_" : ["%n gün önce","%n gün önce"], "last month" : "geçen ay", "_%n month ago_::_%n months ago_" : ["%n ay önce","%n ay önce"], "last year" : "geçen yıl", "_%n year ago_::_%n years ago_" : ["%n yıl önce","%n yıl önce"], "_%n hour ago_::_%n hours ago_" : ["%n saat önce","%n saat önce"], "_%n minute ago_::_%n minutes ago_" : ["%n dakika önce","%n dakika önce"], "seconds ago" : "saniye önce", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "%s kodlu modül bulunamadı. Lütfen uygulamalarınız içinden modülü etkinleştirin ya da sistem yöneticinizle görüşün.", "File name is a reserved word" : "Bu dosya adı sistem kullanıma ayrılmıştır", "File name contains at least one invalid character" : "Dosya adında en az bir geçersiz karakter var", "File name is too long" : "Dosya adı çok uzun", "Dot files are not allowed" : "Nokta dosyalarına izin verilmiyor", "Empty filename is not allowed" : "Boş dosya adına izin verilmiyor", "App \"%s\" cannot be installed because appinfo file cannot be read." : "appinfo dosyası okunamadığından \"%s\" uygulaması kurulamaz.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "\"%s\" uygulaması sunucu sürümüyle uyumlu olmadığından kurulamaz.", "This is an automatically sent email, please do not reply." : "Bu ileti otomatik olarak gönderildiğinden lütfen yanıtlamayın.", "Help" : "Yardım", "Apps" : "Uygulamalar", "Settings" : "Ayarlar", "Log out" : "Oturumu Kapat", "Users" : "Kullanıcılar", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Temel Ayarlar", "Sharing" : "Paylaşım", "Security" : "Güvenlik", "Encryption" : "Şifreleme", "Additional settings" : "Ek ayarlar", "Tips & tricks" : "İpucu ve kolaylıklar", "Personal info" : "Kişisel Bilgiler", "Sync clients" : "Eşitleme istemcileri", "Unlimited" : "Sınırsız", "__language_name__" : "Türkçe", "Verifying" : "Doğrulanıyor", "Verifying …" : "Doğrulanıyor...", "Verify" : "Doğrula", "%s enter the database username and name." : "%s veritabanı adını ve kullanıcı adını yazın.", "%s enter the database username." : "%s veritabanı kullanıcı adını yazın.", "%s enter the database name." : "%s veritabanı adını yazın.", "%s you may not use dots in the database name" : "%s veritabanı adında nokta kullanamayabilirsiniz", "Oracle connection could not be established" : "Oracle bağlantısı kurulamadı", "Oracle username and/or password not valid" : "Oracle kullanıcı adı ya da parolası geçersiz", "PostgreSQL username and/or password not valid" : "PostgreSQL kullanıcı adı ya da parolası geçersiz", "You need to enter details of an existing account." : "Varolan bir hesabın bilgilerini yazmalısınız.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X desteklenmiyor ve %s bu platformda düzgün çalışmayacak. Kullanmaktan doğacak riskler size aittir!", "For the best results, please consider using a GNU/Linux server instead." : "En iyi sonucu almak için GNU/Linux sunucusu kullanın.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Bu %s kopyasının 32-bit PHP ortamında çalıştırıldığı ve open_basedir seçeneğinin php.ini dosyasından ayarlandığı görülüyor. Bu yapılandırma 4 GB boyutundan büyük dosyalarda sorun çıkarır ve kullanılması önerilmez.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Lütfen php.ini dosyasındaki open_basedir ayarını kaldırın ya da 64-bit PHP sürümüne geçin.", "Set an admin username." : "Bir yönetici kullanıcı adı yazın.", "Set an admin password." : "Bir yönetici parolası yazın.", "Can't create or write into the data directory %s" : "%s veri klasörü oluşturulamadı ya da içine yazılamadı", "Invalid Federated Cloud ID" : "Birleşmiş Bulut Kimliği Geçersiz", "Sharing %s failed, because the backend does not allow shares from type %i" : "Arka uç %s türündeki paylaşımlara izin vermediğinden %s paylaşılamadı", "Sharing %s failed, because the file does not exist" : "Dosya bulunamadığından %s paylaşılamadı", "You are not allowed to share %s" : "%s ögesini paylaşma izniniz yok", "Sharing %s failed, because you can not share with yourself" : "%s paylaşılamadı. Ögeyi kendiniz ile paylaşamazsınız", "Sharing %s failed, because the user %s does not exist" : "%s paylaşılamadı. %s kullanıcısı bulunamadı", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s paylaşılamadı. %s kullanıcısı %s ögesinin üyesi olduğu grupların herhangi birinin üyesi değil", "Sharing %s failed, because this item is already shared with %s" : "%s paylaşılamadı. Bu öge %s ile zaten paylaşılmış", "Sharing %s failed, because this item is already shared with user %s" : "%s paylaşılamadı. Bu öge zaten %s kullanıcısı ile paylaşılmış", "Sharing %s failed, because the group %s does not exist" : "%s paylaşılamadı. %s grubu bulunamadı", "Sharing %s failed, because %s is not a member of the group %s" : "%s paylaşılamadı. %s kullanıcısı %s grubunun üyesi değil", "You need to provide a password to create a public link, only protected links are allowed" : "Herkese açık bir bağlantı oluşturmak için bir parola belirtmelisiniz. Yalnız korunmuş bağlantılara izin verilir", "Sharing %s failed, because sharing with links is not allowed" : "%s paylaşılamadı. Bağlantı üzerinden paylaşım izni verilmiyor", "Not allowed to create a federated share with the same user" : "Aynı kullanıcı ile bir birleşmiş paylaşım oluşturulamaz", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s paylaşılamadı. %s bulunamadı. Sunucuya şu anda erişilemiyor olabilir.", "Share type %s is not valid for %s" : "%s paylaşım türü %s için geçerli değil", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Son kullanma tarihi ayarlanamadı. Paylaşımların kullanım süresi paylaşıldıktan %s sonra dolamaz", "Cannot set expiration date. Expiration date is in the past" : "Son kullanma tarihi ayarlanamıyor. Son kullanma tarihi geçmişte", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Paylaşım arka ucu %s OCP\\Share_Backend arayüzünü desteklemeli", "Sharing backend %s not found" : "%s paylaşım arka ucu bulunamadı", "Sharing backend for %s not found" : "%s için paylaşım arka ucu bulunamadı", "Sharing failed, because the user %s is the original sharer" : "Paylaşılamadı. %s kullanıcısı özgün paylaşan kişi", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "%s paylaşılamadı. İzinler %s için verilen izin düzeyini aşıyor", "Sharing %s failed, because resharing is not allowed" : "%s paylaşılamadı. Yeniden paylaşıma izin verilmiyor", "Sharing %s failed, because the sharing backend for %s could not find its source" : "%s paylaşılamadı. Paylaşım arka ucu %s kaynağını bulamadı", "Sharing %s failed, because the file could not be found in the file cache" : "%s paylaşılamadı. Dosyanın dosya ön belleğinde bulunamadı", "Can’t increase permissions of %s" : "%s izinleri arttırılamadı", "Files can’t be shared with delete permissions" : "Silme izni ile dosya paylaşılamaz", "Files can’t be shared with create permissions" : "Ekleme izni ile dosya paylaşılamaz", "Expiration date is in the past" : "Son kullanma tarihi geçmişte", "Can’t set expiration date more than %s days in the future" : "Son kullanma tarihi %sgünden sonrası olarak ayarlanamaz", "%s shared »%s« with you" : "%s sizinle »%s« ögesini paylaştı", "%s shared »%s« with you." : "%s sizinle »%s« ögesini paylaştı.", "Click the button below to open it." : "Açmak için aşağıdaki düğmeye tıklayın.", "Open »%s«" : "»%s« Aç", "%s via %s" : "%s, %s aracılığıyla", "The requested share does not exist anymore" : "Erişilmek istenilen paylaşım artık yok", "Could not find category \"%s\"" : "\"%s\" kategorisi bulunamadı", "Sunday" : "Pazar", "Monday" : "Pazartesi", "Tuesday" : "Salı", "Wednesday" : "Çarşamba", "Thursday" : "Perşembe", "Friday" : "Cuma", "Saturday" : "Cumartesi", "Sun." : "Paz", "Mon." : "Pzt", "Tue." : "Sal", "Wed." : "Çar", "Thu." : "Per", "Fri." : "Cum", "Sat." : "Cmt", "Su" : "Pa", "Mo" : "Pt", "Tu" : "Sa", "We" : "Ça", "Th" : "Pe", "Fr" : "Cu", "Sa" : "Ct", "January" : "Ocak", "February" : "Şubat", "March" : "Mart", "April" : "Nisan", "May" : "Mayıs", "June" : "Haziran", "July" : "Temmuz", "August" : "Ağustos", "September" : "Eylül", "October" : "Ekim", "November" : "Kası", "December" : "Aralı", "Jan." : "Oca", "Feb." : "Şub", "Mar." : "Mar", "Apr." : "Nis", "May." : "May", "Jun." : "Haz", "Jul." : "Tem", "Aug." : "Ağu", "Sep." : "Eyl", "Oct." : "Eki", "Nov." : "Kas", "Dec." : "Ara", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Kullanıcı adında yalnız şu karakterler kullanılabilir: \"a-z\", \"A-Z\", \"0-9\", ve \"_.@-'\"", "A valid username must be provided" : "Geçerli bir kullanıcı adı yazmalısınız", "Username contains whitespace at the beginning or at the end" : "Kullanıcı adının başı ya da sonunda boşluk var", "Username must not consist of dots only" : "Kullanıcı adı yalnız noktalardan oluşamaz", "A valid password must be provided" : "Geçerli bir parola yazmalısınız", "The username is already being used" : "Bu kullanıcı adı zaten var", "Could not create user" : "Kullanıcı oluşturulamadı", "User disabled" : "Kullanıcı devre dışı", "Login canceled by app" : "Oturum açma uygulama tarafından iptal edildi", "No app name specified" : "Uygulama adı belirtilmemiş", "App '%s' could not be installed!" : "'%s' uygulaması kurulamadı!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "\"%s\" uygulaması, şu gereklilikler sağlanmadığı için kurulamıyor: %s", "a safe home for all your data" : "verileriniz için güvenli bir barınak", "File is currently busy, please try again later" : "Dosya şu anda meşgul, lütfen daha sonra deneyin", "Can't read file" : "Dosya okunamadı", "Application is not enabled" : "Uygulama etkinleştirilmemiş", "Authentication error" : "Kimlik doğrulama sorunu", "Token expired. Please reload page." : "Kodun süresi dolmuş. Lütfen sayfayı yenileyin.", "Unknown user" : "Kullanıcı bilinmiyor", "No database drivers (sqlite, mysql, or postgresql) installed." : "Herhangi bir veritabanı sürücüsü (sqlite, mysql ya da postgresql) kurulmamış.", "Cannot write into \"config\" directory" : "\"config\" klasörüne yazılamıyor", "Cannot write into \"apps\" directory" : "\"apps\" klasörüne yazılamıyor", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Bu sorun genellikle, web sunucusuna apps klasörüne yazma izni verilerek ya da yapılandırma dosyasından uygulama mağazası devre dışı bırakılarak çözülebilir. %s bölümüne bakın", "Cannot create \"data\" directory" : "\"data\" klasörü oluşturulamadı", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Bu sorun genellikle, web sunucusuna kök klasöre yazma izni verilerek çözülebilir. %s bölümüne bakın", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "İzinler genellikle, web sunucusuna kök klasöre yazma izni verilerek düzeltilebilir. %s bölümüne bakın.", "Setting locale to %s failed" : "Dil %s olarak ayarlanamadı", "Please install one of these locales on your system and restart your webserver." : "Lütfen bu dillerden birini sisteminize kurun ve web sunucunuzu yeniden başlatın.", "Please ask your server administrator to install the module." : "Lütfen modülü kurması için sunucu yöneticinizle görüşün.", "PHP module %s not installed." : "PHP %s modülü kurulmamış.", "PHP setting \"%s\" is not set to \"%s\"." : "\"%s\" PHP ayarı \"%s\" olarak ayarlanmamış.", "Adjusting this setting in php.ini will make Nextcloud run again" : "php.ini dosyasında bu ayar yapıldığında Nextcloud yeniden çalışır", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload, beklenen \"0\" değeri yerine \"%s\" olarak ayarlanmış", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bu sorunu çözmek için php.ini dosyasındaki <code>mbstring.func_overload</code> seçeneğini <code>0</code> olarak ayarlayın", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 sürümü en az 2.7.0 olmalıdır. Şu anda %s kurulu.", "To fix this issue update your libxml2 version and restart your web server." : "Bu sorunu çözmek için libxml2 sürümünüzü güncelleyin ve web sunucusunu yeniden başlatın.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP girintili doc bloklarını ayıklamak üzere yapılandırılmış gibi görünüyor. Bu durum bazı çekirdek uygulamalarına erişilmesini engelleyecek.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Bu sorun genellikle Zend OPcache ya da eAccelerator gibi bir ön bellek/hızlandırıcı nedeniyle ortaya çıkar.", "PHP modules have been installed, but they are still listed as missing?" : "PHP modülleri kurulmuş, ancak hala eksik olarak mı görünüyor?", "Please ask your server administrator to restart the web server." : "Lütfen web sunucusunu yeniden başlatması için sunucu yöneticinizle görüşün.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 gerekli", "Please upgrade your database version" : "Lütfen veritabanı sürümünüzü yükseltin", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Lütfen izinleri 0770 olarak ayarlayarak diğer kullanıcıların klasörü görebilmesini sağlayın.", "Your data directory is readable by other users" : "Veri klasörünüz diğer kullanıcılar tarafından okunabilir", "Your data directory must be an absolute path" : "Veri klasörünüz mutlak bir yol olmalıdır", "Check the value of \"datadirectory\" in your configuration" : "Yapılandırmanızdaki \"datadirectory\" seçeneğini denetleyin", "Your data directory is invalid" : "Veri klasörünüz geçersiz", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Veri klasörü kökünde \".ocdata\" adında bir dosya bulunduğundan emin olun.", "Could not obtain lock type %d on \"%s\"." : "\"%s\" için %d kilit türü alınamadı.", "Storage unauthorized. %s" : "Depolamaya erişim izni yok. %s", "Storage incomplete configuration. %s" : "Depolama yapılandırması tamamlanmamış. %s", "Storage connection error. %s" : "Depolama bağlantısı sorunu. %s", "Storage is temporarily not available" : "Depolama geçici olarak kullanılamıyor", "Storage connection timeout. %s" : "Depolama bağlantısı zaman aşımı. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Bu sorun genellikle, %sweb sunucusuna config klasörüne yazma izni verilerek%s çözülebilir.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "%s kodlu modül bulunamadı. Lütfen uygulamalarınız içinden modülü etkinleştirin ya da sistem yöneticinizle görüşün.", "Server settings" : "Sunucu ayarları", "DB Error: \"%s\"" : "Veritabanı Sorunu: \"%s\"", "Offending command was: \"%s\"" : "Saldırgan komut: \"%s\"", "You need to enter either an existing account or the administrator." : "Varolan bir hesap ya da yönetici hesabı yazmalısınız.", "Offending command was: \"%s\", name: %s, password: %s" : "Saldırgan komut: \"%s\", kullanıcı adı: %s, parola: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "%s için izinler ayarlanamadı. İzinler %s için verilmiş izin düzeyini aşıyor", "Setting permissions for %s failed, because the item was not found" : "%s için izinler ayarlanamadı. Öge bulunamadı", "Cannot clear expiration date. Shares are required to have an expiration date." : "Son kullanım tarihi temizlenemiyor. Paylaşımların bir son kullanma tarihi olmalıdır.", "Cannot increase permissions of %s" : "%s izinleri yükseltilemiyor", "Files can't be shared with delete permissions" : "Dosyalar silme izniyle paylaşılamaz", "Files can't be shared with create permissions" : "Dosyalar oluşturma izniyle paylaşılamaz", "Cannot set expiration date more than %s days in the future" : "Paylaşımların son kullanım süreleri, gelecekte %s günden fazla olamaz", "Personal" : "Kişisel", "Admin" : "Yönetici", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Bu sorun genellikle, %sweb sunucusuna apps klasörüne yazma izni verilerek%s çözülebilir.", "Cannot create \"data\" directory (%s)" : "\"Veri\" klasörü oluşturulamadı (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Bu sorun genellikle, <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">web sunucusuna kök klasöre yazma izni verilerek</a> çözülebilir.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "İzinler genellikle, %sweb sunucusuna kök klasöre yazma izni verilerek%s düzeltilebilir.", "Data directory (%s) is readable by other users" : "Veri klasörü (%s) diğer kullanıcılar tarafından okunabilir", "Data directory (%s) must be an absolute path" : "Veri klasörü (%s) mutlak bir yol olmalıdır", "Data directory (%s) is invalid" : "Veri klasörü (%s) geçersiz", "Please check that the data directory contains a file \".ocdata\" in its root." : "Lütfen veri klasörünün kökünde \".ocdata\" dosyasının bulunduğunu denetleyin." },"pluralForm" :"nplurals=2; plural=(n > 1);" } l10n/sq.js 0000604 00000056203 15247130447 0006310 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Nuk shkruhet dot te drejtoria \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Zakonisht kjo mund të ndreqet duke i akorduar shërbyesit web të drejta shkrimi mbi drejtorinë e formësimeve", "See %s" : "Shihni %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Kjo zakonisht mund të rregullohet duke i dhënë serverit të web-it akses shkrimi tek direktoria config. Shih %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Skedarët e aplikacionit %$1s nuk u zëvëndësuan në mënyrë korrekte. Sigurohuni që është një version që përputhet me serverin.", "Sample configuration detected" : "U gjet formësim shembull", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "U pa se është kopjuar shembulli për formësime. Kjo mund të prishë instalimin tuaj dhe nuk mbulohet. Ju lutemi, lexoni dokumentimin, përpara se të kryeni ndryshime te config.php", "%1$s and %2$s" : "%1$s dhe %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s dhe %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s dhe %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s dhe %5$s", "Education Edition" : "Variant Edukativ", "Enterprise bundle" : "Pakoja e ndërmarrjeve", "Groupware bundle" : "Pako groupware", "Social sharing bundle" : "Pakoja e ndarjes sociale", "PHP %s or higher is required." : "Kërkohet PHP %s ose më sipër.", "PHP with a version lower than %s is required." : "Lypset PHP me një version më të ulët se sa %s.", "%sbit or higher PHP required." : "Lypset PHP %sbit ose më i ri.", "Following databases are supported: %s" : "Mbulohen bazat vijuese të të dhënave: %s", "The command line tool %s could not be found" : "Mjeti rresht urdhrash %s s’u gjet dot", "The library %s is not available." : "Libraria %s s’është e passhme.", "Library %s with a version higher than %s is required - available version %s." : "Kërkohet librari %s me një version më të madh se %s - version gati %s.", "Library %s with a version lower than %s is required - available version %s." : "Lypset librari %s me një version më të vogël se %s - version gati %s.", "Following platforms are supported: %s" : "Mbulohen platformat vijuese: %s", "Server version %s or higher is required." : "Versioni i serverit kërkohet %s ose më lartë", "Server version %s or lower is required." : "Versioni i serverit kërkohet %s ose më poshtë", "Unknown filetype" : "Lloj i panjohur skedari", "Invalid image" : "Figurë e pavlefshme", "Avatar image is not square" : "Imazhi avatar nuk është katror", "today" : "sot", "yesterday" : "dje", "_%n day ago_::_%n days ago_" : ["%n ditë më parë","%n ditë më parë"], "last month" : "muajin e shkuar", "_%n month ago_::_%n months ago_" : ["%n muaj më parë","%n muaj më parë"], "last year" : "vitin e shkuar", "_%n year ago_::_%n years ago_" : ["%n vit më parë","%n vjet më parë"], "_%n hour ago_::_%n hours ago_" : ["%n orë më parë","%n orë më parë"], "_%n minute ago_::_%n minutes ago_" : ["%n minutë më parë","%n minuta më parë"], "seconds ago" : "sekonda më parë", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduli me ID: %s nuk ekziston. Ju lutem aktivizojeni atë në konfigurimet e aplikacionit tuaj ose kontaktoni administratorin tuaj.", "File name is a reserved word" : "Emri i kartelës është një emër i rezervuar", "File name contains at least one invalid character" : "Emri i kartelës përmban të paktën një shenjë të pavlefshme", "File name is too long" : "Emri i kartelës është shumë i gjatë", "Dot files are not allowed" : "Nuk lejohen kartela të fshehura", "Empty filename is not allowed" : "Nuk lejohen emra të zbrazët kartelash", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Aplikacioni \"%s\" s’mund të instalohet, ngaqë s’lexohet dot kartela appinfo.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikacioni \"%s\" nuk mund të instalohet sepse nuk përputhet me këtë version të serverit.", "This is an automatically sent email, please do not reply." : "Ky është një email i dërguar automatikisht, ju lutem mos u përgjigjni.", "Help" : "Ndihmë", "Apps" : "Aplikacione", "Settings" : "Konfigurime", "Log out" : "Shkyçu", "Users" : "Përdorues", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Konfigurime bazike", "Sharing" : "Ndarja", "Security" : "Siguria", "Encryption" : "Enkriptimi", "Additional settings" : "Konfigurime shtesë", "Tips & tricks" : "Këshilla dhe rrengje", "Personal info" : "Informacion personal", "Sync clients" : "Klientë të sikronizuar", "Unlimited" : "E palimituar", "__language_name__" : "_emri_i_gjuhës__", "Verifying" : "Duke e verifikuar", "Verifying …" : "Duke e verifikuar ...", "Verify" : "Verifiko", "%s enter the database username and name." : "%s jepni emrin e bazës së të dhënave dhe emrin e përdoruesit për të.", "%s enter the database username." : "%s jepni emrin e përdoruesit të bazës së të dhënave.", "%s enter the database name." : "%s jepni emrin e bazës së të dhënave.", "%s you may not use dots in the database name" : "%s s’mund të përdorni pika te emri i bazës së të dhënave", "Oracle connection could not be established" : "S’u vendos dot lidhje me Oracle", "Oracle username and/or password not valid" : "Emër përdoruesi dhe/ose fjalëkalim Oracle-i i pavlefshëm", "PostgreSQL username and/or password not valid" : "Emër përdoruesi dhe/ose fjalëkalim PostgreSQL jo të vlefshëm", "You need to enter details of an existing account." : "Duhet të futni detajet e një llogarie ekzistuese.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nuk mbulohet dhe %s s’do të funksionojë si duhet në këtë platformë. Përdoreni nën përgjegjësinë tuaj! ", "For the best results, please consider using a GNU/Linux server instead." : "Për përfundimet më të mira, ju lutemi, më mirë konsideroni përdorimin e një shërbyesi GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Duket se kjo instancë %s xhiron një mjedis PHP 32-bitësh dhe open_basedir është e formësuar, te php.ini. Kjo do të shpjerë në probleme me kartela më të mëdha se 4 GB dhe këshillohet me forcë të mos ndodhë.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Ju lutemi, hiqeni rregullimin open_basedir nga php.ini juaj ose hidhuni te PHP për 64-bit.", "Set an admin username." : "Caktoni një emër përdoruesi për përgjegjësin.", "Set an admin password." : "Caktoni një fjalëkalim për përgjegjësin.", "Can't create or write into the data directory %s" : "S’e krijon ose s’shkruan dot te drejtoria e të dhënave %s", "Invalid Federated Cloud ID" : "ID Federated Cloud e pavlefshme", "Sharing %s failed, because the backend does not allow shares from type %i" : "Ndarja e %s dështoi, ngaqë pjesa përgjegjëse e shërbyesit nuk lejon ndarje prej llojit %i", "Sharing %s failed, because the file does not exist" : "Ndarja e %s me të tjerët dështoi, ngaqë kartela s’ekziston", "You are not allowed to share %s" : "Nuk ju lejohet ta ndani %s me të tjerët", "Sharing %s failed, because you can not share with yourself" : "Ndarja e %s dështoi, ngaqë s’mund të ndani gjëra me vetveten", "Sharing %s failed, because the user %s does not exist" : "Ndarja e %s me të tjerët dështoi, ngaqë përdoruesi %s nuk ekziston", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Ndarja për %s dështoi, ngaqë përdoruesi %s s’është anëtar i ndonjë grupi ku %s është anëtar", "Sharing %s failed, because this item is already shared with %s" : "Ndarja për %s dështoi, ngaqë ky objekt është ndarë një herë me %s", "Sharing %s failed, because this item is already shared with user %s" : "Ndarja e %s me të tjerët dështoi, ngaqë ky objekt është ndarë tashmë me përdoruesin %s", "Sharing %s failed, because the group %s does not exist" : "Ndarja e %s me të tjerët dështoi, ngaqë grupi %s nuk ekziston", "Sharing %s failed, because %s is not a member of the group %s" : "Ndarja e %s me të tjerët dështoi, ngaqë %s s’është anëtar i grupit %s", "You need to provide a password to create a public link, only protected links are allowed" : "Lypset të jepni një fjalëkalim që të krijoni një lidhje publike, lejohen vetëm lidhje të mbrojtura", "Sharing %s failed, because sharing with links is not allowed" : "Ndarja e %s me të tjerët dështoi, ngaqë nuk lejohet ndarja me lidhje", "Not allowed to create a federated share with the same user" : "S’i lejohet të krijojë një ndarje të federuar me të njëjtin përdorues", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Ndarja për %s dështoi, s’u gjet dot %s, ndoshta shërbyesi është hëpërhë jashtë pune.", "Share type %s is not valid for %s" : "Lloji i ndarjes %s s’është i vlefshëm për %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "S’caktohet dot data e skadimit. Ndarjet s’mund të skadojnë më vonë se %s pasi të jenë ofruar", "Cannot set expiration date. Expiration date is in the past" : "S’caktohet dot data e skadimit. Data e skadimit bie në të kaluarën", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Mekanizmi i shërbimit për ndarje %s duhet të sendërtojë ndërfaqen OCP\\Share_Backend", "Sharing backend %s not found" : "S’u gjet mekanizmi i shërbimit për ndarje %s", "Sharing backend for %s not found" : "S’u gjet mekanizmi i shërbimit për ndarje për %s", "Sharing failed, because the user %s is the original sharer" : "Ndarja dështoi, ngaqë përdoruesi %s është ai që e ndau fillimisht", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Ndarja e %s me të tjerët dështoi, ngaqë lejet tejkalojnë lejet e akorduara për %s", "Sharing %s failed, because resharing is not allowed" : "Ndarja e %s me të tjerët dështoi, ngaqë nuk lejohen rindarje", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Ndarja e %s dështoi, ngaqë mekanizmi i shërbimit për ndarje për %s s’gjeti dot burimin për të", "Sharing %s failed, because the file could not be found in the file cache" : "Ndarja e %s me të tjerët dështoi, ngaqë kartela s’u gjet dot te fshehtina e kartelave", "Can’t increase permissions of %s" : "Nuk mund të shtohen lejet e %s", "Files can’t be shared with delete permissions" : "Skedarët nuk mund të ndahen me leje të fshira", "Files can’t be shared with create permissions" : "matchSkedarët nuk mund të ndahen me leje të krijuara", "Expiration date is in the past" : "Data e skadimit bie në të kaluarën", "Can’t set expiration date more than %s days in the future" : "Nuk mund të caktohet data e skadimit më shumë se %s ditë në të ardhmen", "%s shared »%s« with you" : "%s ndau me ju »%s«", "%s shared »%s« with you." : "1 %s ndarë »1 %s« me ju.", "Click the button below to open it." : "Kliko butonin më poshtë për të hapur atë.", "Open »%s«" : "Hap»1 %s«", "%s via %s" : "%s përmes %s", "The requested share does not exist anymore" : "Ndarja e kërkuar nuk ekziston më", "Could not find category \"%s\"" : "S’u gjet kategori \"%s\"", "Sunday" : "E Dielë", "Monday" : "E Hënë", "Tuesday" : "E Martë", "Wednesday" : "E Mërkurë", "Thursday" : "E Enjte", "Friday" : "E Premte", "Saturday" : "E Shtunë", "Sun." : "Die.", "Mon." : "Hën.", "Tue." : "Mar.", "Wed." : "Mër.", "Thu." : "Enj.", "Fri." : "Pre.", "Sat." : "Sht.", "Su" : "Di", "Mo" : "Hë", "Tu" : "Ma", "We" : "Ne", "Th" : "En", "Fr" : "Pr", "Sa" : "Sh", "January" : "Janar", "February" : "Shkurt", "March" : "Mars", "April" : "Prill", "May" : "Maj", "June" : "Qershor", "July" : "Korrik", "August" : "Gusht", "September" : "Shtator", "October" : "Tetor", "November" : "Nëntor", "December" : "Dhjetor", "Jan." : "Jan.", "Feb." : "Shk.", "Mar." : "Mar.", "Apr." : "Pri.", "May." : "Maj.", "Jun." : "Qer.", "Jul." : "Kor.", "Aug." : "Gus.", "Sep." : "Sht.", "Oct." : "Tet.", "Nov." : "Nën.", "Dec." : "Dhj.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Në një emër përdoruesi lejohen vetëm shenjat vijuese: \"a-z\", \"A-Z\", \"0-9\", dhe \"_.@-\"", "A valid username must be provided" : "Duhet dhënë një emër i vlefshëm përdoruesi", "Username contains whitespace at the beginning or at the end" : "Emri i përdoruesit përmban hapësirë në fillim ose në fund", "Username must not consist of dots only" : "Emri i përdoruesit nuk duhet të përbëhet vetëm nga pika", "A valid password must be provided" : "Duhet dhënë një fjalëkalim i vlefshëm", "The username is already being used" : "Emri i përdoruesit është tashmë i përdorur", "User disabled" : "Përdorues i çaktivizuar", "Login canceled by app" : "Hyrja u anulua nga aplikacioni", "No app name specified" : "S’u dha emër aplikacioni", "App '%s' could not be installed!" : "Aplikacioni \"%s\" nuk mund të instalohet!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Përditësimi \"%s\" s’instalohet dot, ngaqë s’plotësohen varësitë vijuese: %s.", "a safe home for all your data" : "Një shtëpi e sigurt për të dhënat e tua", "File is currently busy, please try again later" : "Kartela tani është e zënë, ju lutemi, riprovoni më vonë.", "Can't read file" : "S'lexohet dot kartela", "Application is not enabled" : "Aplikacioni s’është aktivizuar", "Authentication error" : "Gabim mirëfilltësimi", "Token expired. Please reload page." : "Token-i ka skaduar. Ju lutem ringarkoni faqen.", "Unknown user" : "Përdorues i panjohur", "No database drivers (sqlite, mysql, or postgresql) installed." : "S’ka baza të dhënash (sqlite, mysql, ose postgresql) të instaluara.", "Cannot write into \"config\" directory" : "S’shkruhet dot te drejtoria \"config\"", "Cannot write into \"apps\" directory" : "S’shkruhet dot te drejtoria \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Zakonisht kjo mund të rregullohet duke i dhënë serverit të web-it akses shkrimi tek direktoria e aplikacioneve ose duke çaktivizuar appstore në skedarin config. Shih %s", "Cannot create \"data\" directory" : "Nuk mund të krijohet direktoria \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Kjo zakonisht mund të rregullohet duke i dhënë serverit të web-it akses shkrimi tek direktoria rrënjë. Shih %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Zakonisht lejet mund të rregullohen duke i dhënë serverit të web-it akses shkrimi tek direktoria rrënjë. Shih %s.", "Setting locale to %s failed" : "Caktimi i gjuhës si %s dështoi", "Please install one of these locales on your system and restart your webserver." : "Ju lutemi, instaloni te sistemi juaj një prej këtyre vendoreve dhe rinisni shërbyesin tuaj web.", "Please ask your server administrator to install the module." : "Ju lutemi, kërkojini përgjegjësit të shërbyesit ta instalojë modulin.", "PHP module %s not installed." : "Moduli PHP %s s’është i instaluar.", "PHP setting \"%s\" is not set to \"%s\"." : "Rregullimi PHP \"%s\" s’është vënë si \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Përshtatja e këtij konfigurimi në php.ini do e bëjë Nextcloud të punoj përsëri", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload është caktuar si \"%s\", në vend të vlerës së pritshme \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Për ta ndrequr këtë problem, caktoni për <code>mbstring.func_overload</code> vlerën <code>0</code> te php.ini juaj", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Lypset të paktën libxml2 2.7.0. Hëpërhë e instaluar është %s.", "To fix this issue update your libxml2 version and restart your web server." : "Për të ndrequr këtë problem, përditësoni libxml2 dhe rinisni shërbyesin tuaj web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Me sa duket, PHP-ja është rregulluar që të heqë blloqe të brendshëm dokumentimi. Kjo do t’i nxjerrë nga funksionimi disa aplikacione bazë.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Kjo ka gjasa të jetë shkaktuar nga një fshehtinë/përshpejtues i tillë si Zend OPcache ose eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Modulet PHP janë instaluar, por tregohen ende sikur mungojnë?", "Please ask your server administrator to restart the web server." : "Ju lutemi, kërkojini përgjegjësit të shërbyesit tuaj të rinisë shërbyesin web.", "PostgreSQL >= 9 required" : "Lypset PostgreSQL >= 9", "Please upgrade your database version" : "Ju lutemi, përmirësoni bazën tuaj të të dhënave me një version më të ri.", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Ju lutemi, kalojani lejet në 0770, që kështu atë drejtori të mos mund ta shfaqin përdorues të tjerë.", "Your data directory is readable by other users" : "Direktoria juaj e të dhënave është e lexueshme nga përdorues të tjerë", "Your data directory must be an absolute path" : "Direktoria juaj e të dhënave duhet të jetë një path absolut", "Check the value of \"datadirectory\" in your configuration" : "Kontrolloni vlerën e \"datadirectory\" te formësimi juaj", "Your data directory is invalid" : "Direktoria juaj e të dhënave është i pavlefshëm", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Sigurohu që ekziston një skedar i quajtur \".ocdata\" në rrënjën e direktorisë së të dhënave.", "Could not obtain lock type %d on \"%s\"." : "S’u mor dot lloj kyçjeje %d në \"%s\".", "Storage unauthorized. %s" : "Depozitë e paautorizuar. %s", "Storage incomplete configuration. %s" : "Formësim jo i plotë i depozitës. %s", "Storage connection error. %s" : "Gabim lidhje te depozita. %s", "Storage is temporarily not available" : "Hapsira ruajtëse nuk është në dispozicion përkohësisht", "Storage connection timeout. %s" : "Mbarim kohe lidhjeje për depozitën. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Zakonisht kjo mund të ndreqet duke %si akorduar shërbyesit web të drejta shkrimi mbi drejtorinë e formësimeve%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "S’ka modul me id: %s. Ju lutemi, aktivizojeni te rregullimet tuaja për aplikacionin ose lidhuni me përgjegjësin tuaj.", "Server settings" : "Konfigurimi i serverit", "DB Error: \"%s\"" : "Gabim DB-je: \"%s\"", "Offending command was: \"%s\"" : "Urdhëri shkaktar ishte: \"%s\"", "You need to enter either an existing account or the administrator." : "Lypset të jepni ose një llogari ekzistuese, ose llogarinë e përgjegjësit.", "Offending command was: \"%s\", name: %s, password: %s" : "Urdhri shkaktar qe: \"%s\", emër: %s, fjalëkalim: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Caktimi i lejeve për %s dështoi, ngaqë lejet tejkalojnë lejet e akorduara për %s", "Setting permissions for %s failed, because the item was not found" : "Caktimi i lejeve për %s dështoi, ngaqë s’u gjet objekti", "Cannot clear expiration date. Shares are required to have an expiration date." : "S’hiqet dot data e skadimit. Ndarjet lypse të kenë një datë skadimi.", "Cannot increase permissions of %s" : "S’mund të shtohen lejet për %s", "Files can't be shared with delete permissions" : "Kartelat s’mund të ndahen me leje fshirjeje", "Files can't be shared with create permissions" : "Kartelat s’mund të ndahen me leje krijimi", "Cannot set expiration date more than %s days in the future" : "S’mund të caktohet data e skadimit më shumë se %s ditë në të ardhmen", "Personal" : "Personale", "Admin" : "Admin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Zakonisht kjo mund të ndreqet duke %si akorduar shërbyesit web të drejta shkrimi mbi drejtorinë e aplikacionit%s ose duke e çaktivizuar appstore-in te kartela e formësimit.", "Cannot create \"data\" directory (%s)" : "S’krijohet dot drejtoria \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Zakonisht kjo mund të ndreqet duke <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">i akorduar shërbyesit web të drejta shkrimi mbi drejtorinë rrënjë</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Zakonisht lejet mund të ndreqen duke %si akorduar shërbyesit web të drejta shkrimi mbi drejtorinë rrënjë%s.", "Data directory (%s) is readable by other users" : "Drejtoria e të dhënave (%s) është e lexueshme nga përdorues të tjerë", "Data directory (%s) must be an absolute path" : "Drejtoria e të dhënave (%s) duhet të jepë një shteg absolut", "Data directory (%s) is invalid" : "Drejtoria e të dhënave (%s) është e pavlefshme", "Please check that the data directory contains a file \".ocdata\" in its root." : "Ju lutemi, kontrolloni që drejtoria e të dhënave përmban në rrënjën e saj një kartelë \".ocdata\"." }, "nplurals=2; plural=(n != 1);"); l10n/fi.json 0000604 00000043630 15247130447 0006620 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Hakemistoon \"config\" kirjoittaminen ei onnistu!", "This can usually be fixed by giving the webserver write access to the config directory" : "Tämän voi yleensä korjata antamalla http-palvelimelle kirjoitusoikeuden asetushakemistoon", "See %s" : "Katso %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Sovelluksen %$1s tiedostoja ei vaihdettu oikein. Varmista että sen versio on yhteensopiva palvelimen kanssa.", "Sample configuration detected" : "Esimerkkimääritykset havaittu", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "On havaittu, että esimerkkimäärityksen on kopioitu. Se voi rikkoa asennuksesi, eikä sitä tueta. Lue ohjeet ennen kuin muutat config.php tiedostoa.", "%1$s and %2$s" : "%1$s ja %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s ja %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s ja %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s ja %5$s", "PHP %s or higher is required." : "PHP %s tai sitä uudempi vaaditaan.", "PHP with a version lower than %s is required." : "PHP versiota %s alempi tarvitaan.", "%sbit or higher PHP required." : "%s-bit tai korkeampi PHP vaaditaan.", "Following databases are supported: %s" : "Seuraavat tietokannat ovat tuettuja: %s", "The command line tool %s could not be found" : "Komentorivityökalua %s ei löytynyt", "The library %s is not available." : "Kirjastoa %s ei ole käytettävissä.", "Library %s with a version higher than %s is required - available version %s." : "Kirjasto %s versiota %s tai uudempi vaaditaan - käytettävissä oleva versio %s.", "Library %s with a version lower than %s is required - available version %s." : "Kirjasto %s versiota alempi %s tarvitaan - käytettävissä oleva versio %s.", "Following platforms are supported: %s" : "Seuraavat alustat ovat tuettuja: %s", "Server version %s or higher is required." : "Palvelinversio %s tai sitä uudempi vaaditaan.", "Server version %s or lower is required." : "Palvelinversio %s tai alhaisempi vaaditaan.", "Unknown filetype" : "Tuntematon tiedostotyyppi", "Invalid image" : "Virheellinen kuva", "Avatar image is not square" : "Avatar-kuva ei ole neliö", "today" : "tänään", "yesterday" : "eilen", "_%n day ago_::_%n days ago_" : ["%n päivä sitten","%n päivää sitten"], "last month" : "viime kuussa", "_%n month ago_::_%n months ago_" : ["%n kuukausi sitten","%n kuukautta sitten"], "last year" : "viime vuonna", "_%n year ago_::_%n years ago_" : ["%n vuosi sitten","%n vuotta sitten"], "_%n hour ago_::_%n hours ago_" : ["%n tunti sitten","%n tuntia sitten"], "_%n minute ago_::_%n minutes ago_" : ["%n minuutti sitten","%n minuuttia sitten"], "seconds ago" : "sekunteja sitten", "File name is a reserved word" : "Tiedoston nimi on varattu sana", "File name contains at least one invalid character" : "Tiedoston nimi sisältää ainakin yhden virheellisen merkin", "File name is too long" : "Tiedoston nimi on liian pitkä", "Dot files are not allowed" : "Pistetiedostot eivät ole sallittuja", "Empty filename is not allowed" : "Tiedostonimi ei voi olla tyhjä", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Sovellusta \"%s\" ei voi asentaa, koska appinfo-tiedostoa ei voi loi lukea.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Sovellusta \"%s\" ei voi asentaa, koska se ei ole yhteensopiva tämän palvelinversion kanssa.", "This is an automatically sent email, please do not reply." : "Tämä on automaattisesti lähetetty viesti. Älä vastaa tähän viestiin.", "Help" : "Ohje", "Apps" : "Sovellukset", "Settings" : "Asetukset", "Log out" : "Kirjaudu ulos", "Users" : "Käyttäjät", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Perusasetukset", "Sharing" : "Jakaminen", "Security" : "Turvallisuus", "Encryption" : "Salaus", "Additional settings" : "Lisäasetukset", "Tips & tricks" : "Vinkkejä", "Personal info" : "Henkilökohtaiset tiedot", "Sync clients" : "Synkronointisovellukset", "Unlimited" : "Rajoittamaton", "__language_name__" : "suomi", "%s enter the database username and name." : "%s anna tietokannan käyttäjätunnus ja nimi.", "%s enter the database username." : "%s anna tietokannan käyttäjätunnus.", "%s enter the database name." : "%s anna tietokannan nimi.", "%s you may not use dots in the database name" : "%s et voi käyttää pisteitä tietokannan nimessä", "Oracle connection could not be established" : "Oracle-yhteyttä ei voitu muodostaa", "Oracle username and/or password not valid" : "Oraclen käyttäjätunnus ja/tai salasana on väärin", "PostgreSQL username and/or password not valid" : "PostgreSQL:n käyttäjätunnus ja/tai salasana on väärin", "You need to enter details of an existing account." : "Anna olemassa olevan tilin tiedot.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X ei ole tuettu, joten %s ei toimi kunnolla tällä alustalla. Käytä omalla vastuulla!", "For the best results, please consider using a GNU/Linux server instead." : "Käytä parhaan lopputuloksen saamiseksi GNU/Linux-palvelinta.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Vaikuttaa siltä, että tämä %s-instanssi toimii 32-bittisessä PHP-ympäristössä ja open_basedir-asetus on määritetty php.ini-tiedostossa. Tämä johtaa ongelmiin yli 4 gigatavun tiedostojen kanssa, eikä siksi ole suositeltavaa.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Poista open_basedir-asetus php.ini-tiedostosta tai vaihda 64-bittiseen PHP:hen.", "Set an admin username." : "Aseta ylläpitäjän käyttäjätunnus.", "Set an admin password." : "Aseta ylläpitäjän salasana.", "Can't create or write into the data directory %s" : "Ei voi luoda tai kirjoittaa data-hakemistoon %s", "Invalid Federated Cloud ID" : "Virheellinen federoidun pilven tunniste", "Sharing %s failed, because the backend does not allow shares from type %i" : "Kohteen %s jakaminen epäonnistui, koska tietovarasto ei salli %i tyyppisiä jakoja", "Sharing %s failed, because the file does not exist" : "Kohteen %s jakaminen epäonnistui, koska tiedostoa ei ole olemassa", "You are not allowed to share %s" : "Oikeutesi eivät riitä kohteen %s jakamiseen.", "Sharing %s failed, because you can not share with yourself" : "Kohteen %s jakaminen epäonnistui, koska et voi jakaa itsesi kanssa", "Sharing %s failed, because the user %s does not exist" : "Kohteen %s jakaminen epäonnistui, koska käyttäjää %s ei ole olemassa", "Sharing %s failed, because this item is already shared with %s" : "Kohteen %s jakaminen epäonnistui, koska kohde on jo jaettu käyttäjän %s kanssa", "Sharing %s failed, because this item is already shared with user %s" : "Kohteen %s jakaminen epäonnistui, koska kohde on jo jaettu käyttäjän %s kanssa", "Sharing %s failed, because the group %s does not exist" : "Kohteen %s jakaminen epäonnistui, koska ryhmää %s ei ole olemassa", "Sharing %s failed, because %s is not a member of the group %s" : "Kohteen %s jakaminen epäonnistui, koska käyttäjä %s ei ole ryhmän %s jäsen", "You need to provide a password to create a public link, only protected links are allowed" : "Anna salasana luodaksesi julkisen linkin. Vain suojatut linkit ovat sallittuja", "Sharing %s failed, because sharing with links is not allowed" : "Kohteen %s jakaminen epäonnistui, koska jakaminen linkkejä käyttäen ei ole sallittu", "Not allowed to create a federated share with the same user" : "Saman käyttäjän kanssa ei ole sallittua luoda federoitua jakoa", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Kohteen %s jakaminen epäonnistui, kohdetta %s ei löytynyt. Kenties palvelin ei ole juuri nyt tavoitettavissa.", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Vanhenemispäivää ei voi asettaa. Jako ei voi vanhentua myöhemmin kuin %s päivää sen jälkeen kun se on jaettu", "Cannot set expiration date. Expiration date is in the past" : "Vanhenemispäivää ei voi asettaa. Vanhenemispäivä on jo mennyt", "Sharing backend %s not found" : "Jakamisen taustaosaa %s ei löytynyt", "Sharing backend for %s not found" : "Jakamisen taustaosaa kohteelle %s ei löytynyt", "Sharing failed, because the user %s is the original sharer" : "Jakaminen epäonnistui, koska käyttäjä %s ei ole alkuperäinen jakaja", "Sharing %s failed, because resharing is not allowed" : "Kohteen %s jakaminen epäonnistui, koska jakaminen uudelleen ei ole sallittu", "Sharing %s failed, because the file could not be found in the file cache" : "Kohteen %s jakaminen epäonnistui, koska tiedostoa ei löytynyt tiedostovälimuistista", "Expiration date is in the past" : "Vanhenemispäivä on menneisyydessä", "%s shared »%s« with you" : "%s jakoi kohteen »%s« kanssasi", "Could not find category \"%s\"" : "Luokkaa \"%s\" ei löytynyt", "Sunday" : "sunnuntai", "Monday" : "maanantai", "Tuesday" : "tiistai", "Wednesday" : "keskiviikko", "Thursday" : "torstai", "Friday" : "perjantai", "Saturday" : "lauantai", "Sun." : "Su", "Mon." : "Ma", "Tue." : "Ti", "Wed." : "Ke", "Thu." : "To", "Fri." : "Pe", "Sat." : "La", "Su" : "Su", "Mo" : "Ma", "Tu" : "Ti", "We" : "Ke", "Th" : "To", "Fr" : "Pe", "Sa" : "La", "January" : "tammikuu", "February" : "helmikuu", "March" : "maaliskuu", "April" : "huhtikuu", "May" : "toukokuu", "June" : "kesäkuu", "July" : "heinäkuu", "August" : "elokuu", "September" : "syyskuu", "October" : "lokakuu", "November" : "marraskuu", "December" : "joulukuu", "Jan." : "Tammi", "Feb." : "Helmi", "Mar." : "Maalis", "Apr." : "Huhti", "May." : "Touko", "Jun." : "Kesä", "Jul." : "Heinä", "Aug." : "Elo", "Sep." : "Syys", "Oct." : "Loka", "Nov." : "Marras", "Dec." : "Joulu", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Vain seuraavat merkit ovat sallittuja käyttäjätunnuksessa: \"a-z\", \"A-Z\", \"0-9\" ja \"_.@-'\"", "A valid username must be provided" : "Anna kelvollinen käyttäjätunnus", "Username contains whitespace at the beginning or at the end" : "Käyttäjätunnus sisältää tyhjätilaa joko alussa tai lopussa", "Username must not consist of dots only" : "Käyttäjänimi ei voi koostua vain pisteistä", "A valid password must be provided" : "Anna kelvollinen salasana", "The username is already being used" : "Käyttäjätunnus on jo käytössä", "User disabled" : "Käyttäjä poistettu käytöstä", "Login canceled by app" : "Kirjautuminen peruttiin sovelluksen toimesta", "No app name specified" : "Sovelluksen nimeä ei määritelty", "App '%s' could not be installed!" : "Sovellusta \"%s\" ei voi asentaa!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Sovelluksen \"%s\" asennus ei onnistu, koska seuraavia riippuvuuksia ei ole täytetty: %s", "a safe home for all your data" : "turvallinen koti kaikille tiedostoillesi", "File is currently busy, please try again later" : "Tiedosto on parhaillaan käytössä, yritä myöhemmin uudelleen", "Can't read file" : "Tiedostoa ei voi lukea", "Application is not enabled" : "Sovellusta ei ole otettu käyttöön", "Authentication error" : "Tunnistautumisvirhe", "Token expired. Please reload page." : "Valtuutus vanheni. Lataa sivu uudelleen.", "Unknown user" : "Tuntematon käyttäjä", "No database drivers (sqlite, mysql, or postgresql) installed." : "Tietokanta-ajureita (sqlite, mysql tai postgresql) ei ole asennettu.", "Cannot write into \"config\" directory" : "Hakemistoon \"config\" kirjoittaminen ei onnistu", "Cannot write into \"apps\" directory" : "Hakemistoon \"apps\" kirjoittaminen ei onnistu", "Cannot create \"data\" directory" : "Hakemiston \"data\" luominen ei onnistu", "Setting locale to %s failed" : "Maa-asetuksen %s asettaminen epäonnistui", "Please install one of these locales on your system and restart your webserver." : "Asenna ainakin yksi kyseisistä maa-asetuksista järjestelmään ja käynnistä http-palvelin uudelleen.", "Please ask your server administrator to install the module." : "Pyydä palvelimen ylläpitäjää asentamaan moduulin.", "PHP module %s not installed." : "PHP-moduulia %s ei ole asennettu.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-asetusta \"%s\" ei ole asetettu arvoon \"%s\".", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload on asetettu arvoon \"%s\" odotetun arvon \"0\" sijaan", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Korjaa tämä ongelma asettamalla <code>mbstring.func_overload</code> arvoon <code>0</code> php.ini-tiedostossasi", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Vähintään libxml2 2.7.0 vaaditaan. %s on asennettu.", "To fix this issue update your libxml2 version and restart your web server." : "Päivitä libxml2:n versio ja käynnistä http-palvelin uudelleen.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Tämä johtuu todennäköisesti välimuistista tai kiihdyttimestä kuten Zend OPcachesta tai eAcceleratorista.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-moduulit on asennettu, mutta ovatko ne vieläkin listattu puuttuviksi?", "Please ask your server administrator to restart the web server." : "Pyydä palvelimen ylläpitäjää käynnistämään web-palvelin uudelleen.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 vaaditaan", "Please upgrade your database version" : "Päivitä tietokantasi versio", "Your data directory is readable by other users" : "Data-hakemisto on muiden käyttäjien luettavissa", "Your data directory must be an absolute path" : "Data-hakemiston tulee olla absoluuttinen polku", "Check the value of \"datadirectory\" in your configuration" : "Tarkista \"datadirectory\"-arvo asetuksistasi", "Your data directory is invalid" : "Datahakemistosi on virheellinen", "Could not obtain lock type %d on \"%s\"." : "Lukitustapaa %d ei saatu kohteelle \"%s\".", "Storage unauthorized. %s" : "Tallennustila ei ole valtuutettu. %s", "Storage incomplete configuration. %s" : "Tallennustilan puutteellinen määritys. %s", "Storage connection error. %s" : "Tallennustilan yhteysvirhe. %s", "Storage is temporarily not available" : "Tallennustila on tilapäisesti pois käytöstä", "Storage connection timeout. %s" : "Tallennustilan yhteyden aikakatkaisu. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Tämän voi yleensä korjata antamalla %shttp-palvelimelle kirjoitusoikeuden asetushakemistoon%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduulia tunnisteella %s ei ole olemassa. Ota se käyttöön sovellusasetuksista tai ota yhteys ylläpitoon.", "Server settings" : "Palvelimen asetukset", "DB Error: \"%s\"" : "Tietokantavirhe: \"%s\"", "Offending command was: \"%s\"" : "Loukkaava komento oli: \"%s\"", "You need to enter either an existing account or the administrator." : "Sinun täytyy antaa joko olemassa oleva tili tai ylläpitäjä.", "Offending command was: \"%s\", name: %s, password: %s" : "Loukkaava komento oli: \"%s\", nimi: %s, salasana: %s", "Setting permissions for %s failed, because the item was not found" : "Kohteen %s oikeuksien asettaminen epäonnistui, koska kohdetta ei löytynyt", "Cannot clear expiration date. Shares are required to have an expiration date." : "Vanhenemispäivän tyhjentäminen ei onnistu. Jaoille on määritelty pakolliseksi vanhenemispäivä.", "Cannot increase permissions of %s" : "Kohteen %s käyttöoikeuksien lisääminen ei onnistu", "Files can't be shared with delete permissions" : "Tiedostoja ei voi jakaa poistamisoikeusilla", "Files can't be shared with create permissions" : "Tiedostoja ei voi jakaa luomisoikeuksilla", "Cannot set expiration date more than %s days in the future" : "Vanhenemispäivä voi olla korkeintaan %s päivän päässä tulevaisuudessa", "Personal" : "Henkilökohtainen", "Admin" : "Ylläpito", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Tämä on yleensä mahdollista korjata %santamalla HTTP-palvelimelle kirjoitusoikeus sovellushakemistoon%s tai poistamalla sovelluskauppa pois käytöstä asetustiedostoa käyttäen.", "Cannot create \"data\" directory (%s)" : "Hakemiston \"data\" luominen ei onnistu (%s)", "Data directory (%s) is readable by other users" : "Data-hakemisto (%s) on muiden käyttäjien luettavissa", "Data directory (%s) must be an absolute path" : "Data-hakemiston (%s) tulee olla absoluuttinen polku", "Data directory (%s) is invalid" : "Data-hakemisto (%s) on virheellinen", "Please check that the data directory contains a file \".ocdata\" in its root." : "Varmista, että data-hakemiston juuressa on tiedosto \".ocdata\"." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/es_AR.js 0000604 00000054620 15247130447 0006657 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "¡No se puede escribir en el directorio \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Esto generalmente se soluciona dándole al servidor web acceso para escribir en el directorio config. ", "See %s" : "Ver %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio config. Favor de ver %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Los archivos de la aplicación %$1s no fueron correctamente remplazados. Favor de asegurarse de que la versión sea compatible con el servidor.", "Sample configuration detected" : "Se ha detectado la configuración de muestra", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Se ha detectado que la configuración de muestra ha sido copiada. Esto puede descomponer su instalacón y no está soportado. Favor de leer la documentación antes de hacer cambios en el archivo config.php", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s y %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s y %5$s", "Enterprise bundle" : "Paquete empresarial", "Groupware bundle" : "Paquete de Groupware", "Social sharing bundle" : "Paquete para compartir en redes sociales", "PHP %s or higher is required." : "Se requiere de PHPH %s o superior.", "PHP with a version lower than %s is required." : "PHP con una versión inferiror a la %s es requerido. ", "%sbit or higher PHP required." : "se requiere PHP para %sbit o superior.", "Following databases are supported: %s" : "Las siguientes bases de datos están soportadas: %s", "The command line tool %s could not be found" : "No fue posible encontar la herramienta de línea de comando %s", "The library %s is not available." : "La biblioteca %s no está disponible. ", "Library %s with a version higher than %s is required - available version %s." : "La biblitoteca %s con una versión superiror a la %s es requerida - versión disponible %s.", "Library %s with a version lower than %s is required - available version %s." : "Se requiere de la biblioteca %s con una versión inferiror a la %s - la versión %s está disponible. ", "Following platforms are supported: %s" : "Las siguientes plataformas están soportadas: %s", "Server version %s or higher is required." : "Se requiere la versión del servidor %s o superior. ", "Server version %s or lower is required." : "La versión del servidor %s o inferior es requerdia. ", "Unknown filetype" : "Tipo de archivo desconocido", "Invalid image" : "Imagen inválida", "Avatar image is not square" : "La imagen del avatar no es un cuadrado", "today" : "hoy", "yesterday" : "ayer", "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"], "last month" : "mes pasado", "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"], "last year" : "año pasado", "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"], "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"], "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"], "seconds ago" : "hace segundos", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Favor de habilitarlo en sus configuraciones de aplicación o contacte a su administrador. ", "File name is a reserved word" : "Nombre de archivo es una palabra reservada", "File name contains at least one invalid character" : "El nombre del archivo contiene al menos un caracter inválido", "File name is too long" : "El nombre del archivo es demasiado largo", "Dot files are not allowed" : "Los archivos Dot no están permitidos", "Empty filename is not allowed" : "El uso de nombres de archivo vacíos no está permitido", "App \"%s\" cannot be installed because appinfo file cannot be read." : "La aplicación \"%s\" no puede ser instalada porque el archivo appinfo no se puede leer. ", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "La aplicación \"%s\" no puede ser instalada porque no es compatible con esta versión del servidor. ", "This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, favor de no contestarlo. ", "Help" : "Ayuda", "Apps" : "Aplicaciones", "Log out" : "Cerrar sesión", "Users" : "Usuarios", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Configuraciones básicas", "Sharing" : "Compartiendo", "Security" : "Seguridad", "Encryption" : "Encripción", "Additional settings" : "Configuraciones adicionales", "Tips & tricks" : "Consejos y trucos", "%s enter the database username and name." : "%s ingrese el nombre del usuario y nombre de la base de datos", "%s enter the database username." : "%s ingresar el nombre de usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s no puede utilizar puntos en el nombre de la base de datos", "Oracle connection could not be established" : "No fue posible establecer la conexión a Oracle", "Oracle username and/or password not valid" : "El nombre de usuario y/o contraseña de Oracle inválidos", "PostgreSQL username and/or password not valid" : "El nombre de usuario y/o contraseña de PostgreSQL inválidos", "You need to enter details of an existing account." : "Necesita ingresar los detalles de una cuenta existente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "OS X de Mac no está soportado y %s no funcionará correctamente en esta plataforma ¡Uselo bajo su propio riesgo!", "For the best results, please consider using a GNU/Linux server instead." : "Para mejores resultados, favor de cosiderar usar en su lugar un servidor GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Al parecer esta instancia %s está corriendo en un ambiente PHP de 32-bits y el open_basedir ha sido configurado en el archivo php.ini. Esto generará problemas con archivos de más de 4GB de tamaño y es altamente desalentado. ", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Favor de eliminar el ajuste open_basedir de su archivo php.ini o cambie a PHP de 64 bits. ", "Set an admin username." : "Configurar un nombre de usuario del administrador", "Set an admin password." : "Establecer la contraseña del administrador.", "Can't create or write into the data directory %s" : "No es posible crear o escribir en el directorio de datos %s", "Invalid Federated Cloud ID" : "ID de Nube Federada Inválido", "Sharing %s failed, because the backend does not allow shares from type %i" : "Se presentó una falla al compartir %s, porque el backend no permite elementos compartidos de tipo %i", "Sharing %s failed, because the file does not exist" : "Se presentó una falla al compartir %s porque el archivo no existe", "You are not allowed to share %s" : "No tiene permitido compartir %s", "Sharing %s failed, because you can not share with yourself" : "Se presento una falla al compartir %s, porque no puede compartir con usted mismo", "Sharing %s failed, because the user %s does not exist" : "Se presentó una falla al compartir %s porque el usuario %s no existe", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Se presentó una falla al compartir %s proque el usuario %s no es un miembro de ninguno de los grupos de los cuales %s es miembro", "Sharing %s failed, because this item is already shared with %s" : "Se presento una falla al compartir %s, porque este elemento ya ha sido compartido con %s", "Sharing %s failed, because this item is already shared with user %s" : "Se presento una falla al compartir %s, porque este elemento ya ha sido compartido con el usuario %s", "Sharing %s failed, because the group %s does not exist" : "Se presentó una falla al compartir %s, porque el grupo %s no existe", "Sharing %s failed, because %s is not a member of the group %s" : "Se presentó una falla al compartir %s debido a que %s no es un miembro del grupo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Usted necesita proporcionar una contraseña para crear un link público, sólo los links protegidos están permitidos. ", "Sharing %s failed, because sharing with links is not allowed" : "Se presentó una falla al compartir %s porque no está permitido compartir con links", "Not allowed to create a federated share with the same user" : "No está permitido crear un elemento compartido federado con el mismo usuario", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Se presentó una falla al compartir %s, no fue posible encontrar %s, tal vez el servidor sea inalcanzable por el momento", "Share type %s is not valid for %s" : "El tipo del elemento compartido %s no es válido para %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "No ha sido posible establecer la fecha de expiración. Los recursos compartidos no pueden expirar después de %s tras haber sido compartidos", "Cannot set expiration date. Expiration date is in the past" : "No ha sido posible establecer la fecha de expiración. La fecha de expiración ya ha pasado", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El backend %s que comparte debe implementar la interface OCP\\Share_Backend", "Sharing backend %s not found" : "No fue encontrado el Backend que comparte %s ", "Sharing backend for %s not found" : "No fue encontrado el Backend que comparte para %s", "Sharing failed, because the user %s is the original sharer" : "Se presento una falla al compartir, porque el usuario %s es quien compartió originalmente", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Se presentó una falla al compartir %s, porque los permisos exceden los permisos otorgados a %s", "Sharing %s failed, because resharing is not allowed" : "Falla al compartir %s debído a que no se permite volver a compartir", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Se presentó una falla al compartir %s porque el backend que comparte %s no pudo encontrar su origen", "Sharing %s failed, because the file could not be found in the file cache" : "Se presentó una falla al compartir %s porque el archivo no se encontró en el caché de archivos", "Expiration date is in the past" : "La fecha de expiración ya ha pasado", "%s shared »%s« with you" : "%s ha compartido »%s« con usted", "%s via %s" : "%s por %s", "Could not find category \"%s\"" : "No fue posible encontrar la categoria \"%s\"", "Sunday" : "Domingo", "Monday" : "Lunes", "Tuesday" : "Martes", "Wednesday" : "Miércoles", "Thursday" : "Jueves", "Friday" : "Viernes", "Saturday" : "Sábado", "Sun." : "Dom.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mie.", "Thu." : "Jue.", "Fri." : "Vie.", "Sat." : "Sab.", "Su" : "Do", "Mo" : "Lu", "Tu" : "Ma", "We" : "Mi", "Th" : "Ju", "Fr" : "Vi", "Sa" : "Sa", "January" : "Enero", "February" : "Febrero", "March" : "Marzo", "April" : "Abril", "May" : "Mayo", "June" : "Junio", "July" : "Julio", "August" : "Agosto", "September" : "Septiembre", "October" : "Octubre", "November" : "Noviembre", "December" : "Diciembre", "Jan." : "Ene.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "May.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Ago.", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Dic.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Sólo se permiten los siguientes caracteres en el nombre de usuario: \"a-z\", \"A-Z\", \"0-9\" y \"_.@-'\"", "A valid username must be provided" : "Se debe proporcionar un nombre de usuario válido", "Username contains whitespace at the beginning or at the end" : "El nombre del usuario contiene un espacio en blanco al inicio o al final", "Username must not consist of dots only" : "El nombre de usuario no debe consistir de solo puntos. ", "A valid password must be provided" : "Se debe proporcionar una contraseña válida", "The username is already being used" : "Ese nombre de usuario ya está en uso", "User disabled" : "Usuario deshabilitado", "Login canceled by app" : "Inicio de sesión cancelado por la aplicación", "No app name specified" : "No se ha especificado el nombre de la aplicación", "App '%s' could not be installed!" : "¡La aplicación \"%s\" no puede ser instalada!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "La aplicación \"%s\" no puede ser instalada porque las siguientes dependencias no están satisfechas: %s ", "a safe home for all your data" : "un lugar seguro para todos sus datos", "File is currently busy, please try again later" : "El archivo se encuentra actualmente en uso, favor de intentarlo más tarde. ", "Can't read file" : "No se puede leer el archivo", "Application is not enabled" : "La aplicación está deshabilitada", "Authentication error" : "Error de autenticación", "Token expired. Please reload page." : "La ficha ha expirado. Favor de recarga la página.", "Unknown user" : "Ususario desconocido", "No database drivers (sqlite, mysql, or postgresql) installed." : "No cuenta con controladores de base de datos (sqlite, mysql o postgresql) instalados. ", "Cannot write into \"config\" directory" : "No fue posible escribir en el directorio \"config\"", "Cannot write into \"apps\" directory" : "No fue posible escribir en el directorio \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio de las aplicaciones o deshabilitando la appstore en el archivo config. Favor de ver %s", "Cannot create \"data\" directory" : "No fue posible crear el directorio \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio raíz. Favor de ver %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Por lo general los permisos se pueden corregir al darle al servidor web acceso de escritura al directorio raíz. Favor de ver %s.", "Setting locale to %s failed" : "Se presentó una falla al establecer la regionalización a %s", "Please install one of these locales on your system and restart your webserver." : "Favor de instalar uno de las siguientes configuraciones locales en su sistema y reinicie su servidor web", "Please ask your server administrator to install the module." : "Favor de solicitar a su adminsitrador la instalación del módulo. ", "PHP module %s not installed." : "El módulo de PHP %s no está instalado. ", "PHP setting \"%s\" is not set to \"%s\"." : "El ajuste PHP \"%s\" no esta establecido a \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "El cambiar este ajuste del archivo php.ini hará que Nextcloud corra de nuevo.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload está establecido como \"%s\" en lugar del valor esperado de \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Para corregir este tema, establezca <code>mbstring.func_overload</code> a <code>0</code> en su archivo php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Se requiere de por lo menos libxml2 2.7.0. Actualmente %s esta instalado. ", "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este tema, favor de actualizar la versión de su libxml2 y reinicie su servidor web. ", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Al parecer PHP está configurado para quitar los bloques de comentarios internos. Esto hará que varias aplicaciones principales sean inaccesibles. ", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Esto ha sido causado probablemente por un acelerador de caché como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "¿Los módulos de PHP han sido instalados, pero se siguen enlistando como faltantes?", "Please ask your server administrator to restart the web server." : "Favor de solicitar al administrador reiniciar el servidor web. ", "PostgreSQL >= 9 required" : "Se requiere PostgreSQL >= 9", "Please upgrade your database version" : "Favor de actualizar la versión de la base de datos", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Favor de cambiar los permisos a 0770 para que el directorio no pueda ser enlistado por otros usuarios. ", "Your data directory is readable by other users" : "Su direcctorio data puede ser leído por otros usuarios", "Your data directory must be an absolute path" : "Su direcctorio data debe ser una ruta absoluta", "Check the value of \"datadirectory\" in your configuration" : "Verifique el valor de \"datadirectory\" en su configuración", "Your data directory is invalid" : "Su directorio de datos es inválido", "Could not obtain lock type %d on \"%s\"." : "No fue posible obtener el tipo de bloqueo %d en \"%s\". ", "Storage unauthorized. %s" : "Almacenamiento no autorizado. %s", "Storage incomplete configuration. %s" : "Configuración incompleta del almacenamiento. %s", "Storage connection error. %s" : "Se presentó un error con la conexión al almacenamiento. %s", "Storage is temporarily not available" : "El almacenamieto se encuentra temporalmente no disponible", "Storage connection timeout. %s" : "Se agotó el tiempo de conexión del almacenamiento. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Esto generalmente se soluciona %s dándole al servidor web acceso para escribir en el directorio config %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con id: %s no existe. Favor de habilitarlo en sus configuraciones de aplicación o contacte a su administrador. ", "Server settings" : "Configuraciones del servidor", "DB Error: \"%s\"" : "Error de BD: \"%s\"", "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", "You need to enter either an existing account or the administrator." : "Necesita ingresar una cuenta ya sea existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando infractor: \"%s\", nombre: %s, contraseña: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Se persentó una falla al establecer los permisos para %s, porque los permisos exceden los permisos otorgados a %s", "Setting permissions for %s failed, because the item was not found" : "Se persentó una falla al establecer los permisos para %s, porque no se encontró el elemento ", "Cannot clear expiration date. Shares are required to have an expiration date." : "No ha sido posible borrar la fecha de expiración. Los elelentos compartidos deben tener una fecha de expiración.", "Cannot increase permissions of %s" : "No se pueden incrementar los permisos de %s", "Files can't be shared with delete permissions" : "No es posible compartir archivos con permisos de borrado", "Files can't be shared with create permissions" : "No es posible compartir archivos con permisos de creación", "Cannot set expiration date more than %s days in the future" : "No es posible establecer la fecha de expiración más allá de %s días en el futuro", "Personal" : "Personal", "Admin" : "Administración", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Esto se puede arreglar por %s al darle acceso de escritura al servidor web al directorio de las aplicaciones %s o al deshabilitar la tienda de aplicaciones en el archivo de configuración", "Cannot create \"data\" directory (%s)" : "No fue posible crear el directorio (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Esto se puede arreglar generalmente al <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">darle al servidor web accesos al directorio raíz</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Los permisos se pueden arreglar generalmente al %s darle al servidor web accesos al direcotiro raíz %s.", "Data directory (%s) is readable by other users" : "El directorio de datos (%s) puede ser leído por otros usuarios", "Data directory (%s) must be an absolute path" : "El directorio de datos (%s) debe ser una ruta absoluta", "Data directory (%s) is invalid" : "El directorio de datos (%s) es inválido", "Please check that the data directory contains a file \".ocdata\" in its root." : "Favor de verificar que el directorio de datos tenga un archivo \".ocdata\" en su raíz. " }, "nplurals=2; plural=(n != 1);"); l10n/nb.json 0000604 00000052471 15247130447 0006624 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Kan ikke skrive til «config»-mappen!", "This can usually be fixed by giving the webserver write access to the config directory" : "Dette kan vanligvis ordnes ved å gi vev-tjeneren skrivetilgang til config-mappen", "See %s" : "Se %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dette kan vanligvis ordnes ved å gi vev-tjeneren skrivetilgang til config-mappen. Se %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Filene i appen %$1s ble ikke erstattet skikkelig. Sjekk at versjonen er kompatibel med tjeneren.", "Sample configuration detected" : "Eksempeloppsett oppdaget", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Det ble oppdaget at eksempeloppsettet er blitt kopiert. Dette kan ødelegge installasjonen din og støttes ikke. Les dokumentasjonen før du gjør endringer i config.php", "%1$s and %2$s" : "%1$s og %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s og %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s og %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s og %5$s", "Education Edition" : "Utdanningsversjon", "Enterprise bundle" : "Bedrifts-pakke", "Groupware bundle" : "Gruppevare-pakke", "Social sharing bundle" : "Sosialdelings-pakke", "PHP %s or higher is required." : "PHP %s eller nyere kreves.", "PHP with a version lower than %s is required." : "PHP med en versjon lavere enn %s kreves.", "%sbit or higher PHP required." : "%sbit eller høyere PHP kreves", "Following databases are supported: %s" : "Følgende databaser støttes: %s", "The command line tool %s could not be found" : "Kommandolinjeverktøyet %s ble ikke funnet", "The library %s is not available." : "Biblioteket %s er ikke tilgjengelig.", "Library %s with a version higher than %s is required - available version %s." : "Bibliotek %s med en versjon høyere enn %s kreves - tilgjengelig versjon %s.", "Library %s with a version lower than %s is required - available version %s." : "Bibliotek %s med en versjon lavere nn %s kreves - tilgjengelig version %s.", "Following platforms are supported: %s" : "Følgende plattformer støttes: %s", "Server version %s or higher is required." : "Tjenerversjon %s eller høyere kreves.", "Server version %s or lower is required." : "Tjenerversjon %s eller lavere kreves.", "Unknown filetype" : "Ukjent filtype", "Invalid image" : "Ugyldig bilde", "Avatar image is not square" : "Avatarbilde er ikke firkantet", "today" : "i dag", "yesterday" : "i går", "_%n day ago_::_%n days ago_" : ["%n dag siden","%n dager siden"], "last month" : "forrige måned", "_%n month ago_::_%n months ago_" : ["for %n måned siden","for %n måneder siden"], "last year" : "forrige år", "_%n year ago_::_%n years ago_" : ["%n år siden","%n år siden"], "_%n hour ago_::_%n hours ago_" : ["for %n time siden","for %n timer siden"], "_%n minute ago_::_%n minutes ago_" : ["for %n minutt siden","for %n minutter siden"], "seconds ago" : "for få sekunder siden", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finnes ikke. Skru den på i programinnstillingene eller kontakt en administrator.", "File name is a reserved word" : "Filnavnet er et reservert ord", "File name contains at least one invalid character" : "Filnavnet inneholder minst ett ulovlig tegn", "File name is too long" : "Filnavnet er for langt", "Dot files are not allowed" : "Punktum-filer er ikke tillatt", "Empty filename is not allowed" : "Tomt filnavn er ikke tillatt", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Programmet \"%s\" kan ikke installeres på grunn av at appinfo-filen ikke kan leses.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Programmet \"%s\" kan ikke installere fordi det ikke er kompatibel med denne tjenerversjonen.", "This is an automatically sent email, please do not reply." : "Dette er en automatisk sendt e-post, ikke svar.", "Help" : "Hjelp", "Apps" : "Programmer", "Settings" : "Innstillinger", "Log out" : "Logg ut", "Users" : "Brukere", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Grunninnstillinger", "Sharing" : "Deling", "Security" : "Sikkerhet", "Encryption" : "Kryptering", "Additional settings" : "Flere innstillinger", "Tips & tricks" : "Tips og triks", "Personal info" : "Personlig informasjon", "Sync clients" : "Synkroniser klienter", "Unlimited" : "Ubegrenset", "__language_name__" : "Norsk bokmål", "Verifying" : "Bekrefter", "Verifying …" : "Bekrefter…", "Verify" : "Bekreft", "%s enter the database username and name." : "%s legg inn database brukernavn og navn.", "%s enter the database username." : "%s legg inn brukernavn for databasen.", "%s enter the database name." : "%s legg inn navnet på databasen.", "%s you may not use dots in the database name" : "%s du kan ikke bruke punktum i databasenavnet", "Oracle connection could not be established" : "Klarte ikke å etablere forbindelse til Oracle", "Oracle username and/or password not valid" : "Oracle-brukernavn og/eller passord er ikke gyldig", "PostgreSQL username and/or password not valid" : "PostgreSQL-brukernavn og/eller passord er ikke gyldig", "You need to enter details of an existing account." : "Du må legge in detaljene til en eksisterende konto.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X støttes ikke og %s vil ikke fungere korrekt på denne plattformen. Bruk på egen risiko!", "For the best results, please consider using a GNU/Linux server instead." : "For beste resultat, vurder å bruke en GNU/Linux-tjener i stedet.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Det ser ut for at %s-instansen kjører i et 32-bit PHP-miljø med open_basedir satt opp i php.ini. Dette vil føre til problemer med filer over 4 GB og frarådes på det sterkeste.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Fjern innstillingen open_basedir i php.ini eller bytt til 64-bit PHP.", "Set an admin username." : "Sett et admin-brukernavn.", "Set an admin password." : "Sett et admin-passord.", "Can't create or write into the data directory %s" : "Kan ikke opprette eller skrive i datamappen %s", "Invalid Federated Cloud ID" : "Ugyldig ID for sammenknyttet sky", "Sharing %s failed, because the backend does not allow shares from type %i" : "Deling av %s mislyktes, fordi tjeneren ikke tillater delinger fra type %i", "Sharing %s failed, because the file does not exist" : "Deling av %s mislyktes, fordi filen ikke eksisterer", "You are not allowed to share %s" : "Du har ikke lov til å dele %s", "Sharing %s failed, because you can not share with yourself" : "Deling av %s mislyktes fordi du ikke kan dele med deg selv", "Sharing %s failed, because the user %s does not exist" : "Deling av %s mislyktes, fordi brukeren %s ikke finnes", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Deling av %s mislyktes, fordi brukeren %s ikke er medlem av noen grupper som %s er medlem av", "Sharing %s failed, because this item is already shared with %s" : "Deling av %s mislyktes, fordi dette elementet allerede er delt med %s", "Sharing %s failed, because this item is already shared with user %s" : "Deling av %s mislyktes, fordi dette elementet allerede er delt med bruker %s", "Sharing %s failed, because the group %s does not exist" : "Deling av %s mislyktes, fordi gruppen %s ikke finnes", "Sharing %s failed, because %s is not a member of the group %s" : "Deling av %s mislyktes, fordi %s ikke er medlem av gruppen %s", "You need to provide a password to create a public link, only protected links are allowed" : "Du må oppgi et passord for å lage en offentlig lenke. Bare beskyttede lenker er tillatt", "Sharing %s failed, because sharing with links is not allowed" : "Deling av %s mislyktes, fordi deling med lenker ikke er tillatt", "Not allowed to create a federated share with the same user" : "Ikke tillatt å opprette en sammenknyttet sky-deling med den samme brukeren", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Deling %s mislyktes, fant ikke %s, kanskje tjeneren er utilgjengelig for øyeblikket.", "Share type %s is not valid for %s" : "Delingstype %s er ikke gyldig for %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Kan ikke sette utøpsdato. Delinger kan ikke utløpe senere enn %s etter at de har blitt delt", "Cannot set expiration date. Expiration date is in the past" : "Kan ikke sette utløpsdato. Utløpsdato er tilbake i tid", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Delings-tjener %s må implementere grensesnittet OCP\\Share_Backend", "Sharing backend %s not found" : "Delings-tjener %s ikke funnet", "Sharing backend for %s not found" : "Delings-tjener for %s ikke funnet", "Sharing failed, because the user %s is the original sharer" : "Deling mislyktes fordi brukeren %s er den som delte opprinnelig", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Deling av %s mislyktes, fordi tillatelsene går utover tillatelsene som er gitt til %s", "Sharing %s failed, because resharing is not allowed" : "Deling av %s mislyktes, fordi videre-deling ikke er tillatt", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Deling av %s mislyktes, fordi delings-bakenden for %s ikke kunne finne kilden", "Sharing %s failed, because the file could not be found in the file cache" : "Deling av %s mislyktes, fordi filen ikke ble funnet i fil-mellomlageret", "Can’t increase permissions of %s" : "Kan ikke øke tillatelser for %s", "Files can’t be shared with delete permissions" : "Filer kan ikke deles med tilgang til sletting", "Files can’t be shared with create permissions" : "Filer kan ikke deles med tilgang til opprettelse", "Expiration date is in the past" : "Utløpsdato er i fortid", "Can’t set expiration date more than %s days in the future" : "Kan ikke sette utløpsdato mer enn %s dager i fremtiden", "%s shared »%s« with you" : "%s delte »%s« med deg", "%s shared »%s« with you." : "%s delte \"%s\" med deg.", "Click the button below to open it." : "Klikk på knappen nedenfor for å åpne den.", "Open »%s«" : "Åpne »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Forespurt ressurs finnes ikke lenger", "Could not find category \"%s\"" : "Kunne ikke finne kategori \"%s\"", "Sunday" : "Søndag", "Monday" : "Mandag", "Tuesday" : "Tirsdag", "Wednesday" : "Onsdag", "Thursday" : "Torsdag", "Friday" : "Fredag", "Saturday" : "Lørdag", "Sun." : "Søn.", "Mon." : "Man.", "Tue." : "Tir.", "Wed." : "Ons.", "Thu." : "Tirs.", "Fri." : "Fre.", "Sat." : "Lør.", "Su" : "Sø", "Mo" : "Ma", "Tu" : "Ti", "We" : "On", "Th" : "To", "Fr" : "Fr", "Sa" : "Lø", "January" : "Januar", "February" : "Februar", "March" : "Mars", "April" : "April", "May" : "Mai", "June" : "Juni", "July" : "Juli", "August" : "August", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "Desember", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Mai.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Des.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Bare disse tegnene tillates i et brukernavn: \"a-z\", \"A-Z\", \"0-9\" og \"_.@-'\"", "A valid username must be provided" : "Oppgi et gyldig brukernavn", "Username contains whitespace at the beginning or at the end" : "Brukernavn inneholder blanke på begynnelsen eller slutten", "Username must not consist of dots only" : "Brukernavn kan ikke bare bestå av punktum", "A valid password must be provided" : "Oppgi et gyldig passord", "The username is already being used" : "Brukernavnet er allerede i bruk", "Could not create user" : "Kunne ikke opprette bruker", "User disabled" : "Brukeren er deaktivert", "Login canceled by app" : "Innlogging avbrutt av app", "No app name specified" : "Intet programnavn spesifisert", "App '%s' could not be installed!" : "Programmet '%s' kunne ikke installeres!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Programmet \"%s\" kan ikke installeres fordi følgende avhengigheter ikke er tilfredsstilt: %s", "a safe home for all your data" : "et sikkert hjem for alle dine data", "File is currently busy, please try again later" : "Filen er opptatt for øyeblikket, prøv igjen senere", "Can't read file" : "Kan ikke lese fil", "Application is not enabled" : "Programmet er ikke påslått", "Authentication error" : "Autentikasjonsfeil", "Token expired. Please reload page." : "Symbol utløpt. Last inn siden på nytt.", "Unknown user" : "Ukjent bruker", "No database drivers (sqlite, mysql, or postgresql) installed." : "Ingen databasedrivere (sqlite, mysql, or postgresql) installert.", "Cannot write into \"config\" directory" : "Kan ikke skrive i \"config\"-mappen", "Cannot write into \"apps\" directory" : "Kan ikke skrive i \"apps\"-mappen", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Dette kan vanligvis ordnes ved å gi vev-tjeneren skrivetilgang til apps-mappen eller ved å skru av programbutikken i config-fila. Se %s", "Cannot create \"data\" directory" : "Kan ikke opprette \"data\"-mappe", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dette kan vanligvis ordnes ved å gi vev-tjeneren skrivetilgang til root-mappen. Se %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Tillatelser kan vanligvis ordnes ved å gi vevtjeneren skrivetilgang til rotmappa. Se %s.", "Setting locale to %s failed" : "Setting av nasjonale innstillinger til %s mislyktes.", "Please install one of these locales on your system and restart your webserver." : "Installer en av disse nasjonale innstillingene på systemet ditt og start vevtjeneren på nytt.", "Please ask your server administrator to install the module." : "Be tjener-administratoren om å installere modulen.", "PHP module %s not installed." : "PHP-modul %s er ikke installert.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-innstilling \"%s\" er ikke satt til \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ved å endre denne innstillingen i php.ini gjør at Nextcloud vil kjøre igjen.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload er satt til \"%s\" i stedet for den forventede verdien \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Sett <code>mbstring.func_overload</code> til <code>0</code> in php.ini for å fikse dette problemet", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Krever minst libxml2 2.7.0. Per nå er %s installert.", "To fix this issue update your libxml2 version and restart your web server." : "For å fikse dette problemet, oppdater din libxml2 versjon og omstart vevtjeneren.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Det ser ut til at at PHP er satt opp til å fjerne innebygde doc-blokker. Dette gjør at flere av kjerneapplikasjonene blir utilgjengelige.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dette forårsakes sannsynligvis av en bufrer/akselerator, som f.eks. Zend OPcache eller eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-moduler har blitt installert, men de listes fortsatt som fraværende?", "Please ask your server administrator to restart the web server." : "Be tjener-administratoren om å starte vevtjeneren på nytt.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 kreves", "Please upgrade your database version" : "Oppgrader databaseversjonen din", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Endre tillatelsene til 0770 slik at mappen ikke kan listes av andre brukere.", "Your data directory is readable by other users" : "Din datamappe kan leses av andre brukere", "Your data directory must be an absolute path" : "Din datamappe må være en absolutt sti", "Check the value of \"datadirectory\" in your configuration" : "Sjekk verdien for \"datadirectory\" i oppsettet ditt", "Your data directory is invalid" : "Din datamappe er ugyldig", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Forsikre deg om at det finnes ei fil kalt \".ocdata\" på rota av datamappa.", "Could not obtain lock type %d on \"%s\"." : "Klarte ikke å låse med type %d på \"%s\".", "Storage unauthorized. %s" : "Lager uautorisert: %s", "Storage incomplete configuration. %s" : "Ikke komplett oppsett for lager. %s", "Storage connection error. %s" : "Tilkoblingsfeil for lager. %s", "Storage is temporarily not available" : "Lagring er midlertidig utilgjengelig", "Storage connection timeout. %s" : "Tidsavbrudd ved tilkobling av lager: %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Dette kan vanligvis ordnes ved %så gi vev-tjeneren skrivetilgang til oppsettsmappen%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finnes ikke. Skru den på i programinnstillingene eller kontakt en administrator.", "Server settings" : "Tjenerinnstillinger", "DB Error: \"%s\"" : "Databasefeil: \"%s\"", "Offending command was: \"%s\"" : "Kommandoen som mislyktes: \"%s\"", "You need to enter either an existing account or the administrator." : "Du må legge inn enten en eksisterende konto eller administratoren.", "Offending command was: \"%s\", name: %s, password: %s" : "Kommandoen som mislyktes var: \"%s\", navn: %s, passord: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Setting av tillatelser for %s mislyktes, fordi tillatelsene gikk ut over tillatelsene som er gitt til %s", "Setting permissions for %s failed, because the item was not found" : "Setting av tillatelser for %s mislyktes, fordi elementet ikke ble funnet", "Cannot clear expiration date. Shares are required to have an expiration date." : "Kan ikke fjerne utløpsdato. Delinger må ha en utløpsdato.", "Cannot increase permissions of %s" : "Kan ikke øke tillatelser for %s", "Files can't be shared with delete permissions" : "Filer kan ikke deles med rettigheter til sletting", "Files can't be shared with create permissions" : "Filer kan ikke deles med rettigheter til å opprette", "Cannot set expiration date more than %s days in the future" : "Kan ikke sette utløpsdato mer enn %s dager fram i tid", "Personal" : "Personlig", "Admin" : "Admin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Dette kan vanligvis ordnes ved %så gi vev-tjeneren skrivetilgang til program-mappen%s eller ved å deaktivere programbutikken i config-filen.", "Cannot create \"data\" directory (%s)" : "Kan ikke opprette \"data\"-mappen (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Dette fikses vanligvis ved å <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">gi vevtjeneren skrivetilgang til rotmappen</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Tillatelser kan vanligvis ordnes ved %så gi vevtjeneren skrivetilgang til rotmappen%s.", "Data directory (%s) is readable by other users" : "Data-mappen (%s) kan leses av andre brukere", "Data directory (%s) must be an absolute path" : "Datamappen (%s) må være en absolutt sti", "Data directory (%s) is invalid" : "Data-mappe (%s) er ugyldig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Sjekk at det ligger en fil \".ocdata\" i roten av data-mappen." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/lt_LT.js 0000604 00000036667 15247130447 0006717 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Nepavyksta rašyti į \"config\" katalogą!", "This can usually be fixed by giving the webserver write access to the config directory" : "Tai, dažniausiai, gali būti ištaisyta suteikiant saityno serveriui rašymo prieigą prie konfigūracijos katalogo", "See %s" : "Žiūrėkite %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Tai, dažniausiai, gali būti pataisyta, suteikiant saityno serveriui rašymo prieigą prie konfigūracijos katalogo. Žiūrėkite %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Programėlės %$1s failai buvo pakeisti neteisingai. Įsitikinkite, kad versija yra suderinama su serveriu.", "Sample configuration detected" : "Aptiktas konfigūracijos pavyzdys", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Pastebėta, kad nukopijuota pavyzdinė konfigūracija. Tai gali pažeisti jūsų diegimą ir yra nepalaikoma. Prieš atliekant pakeitimus config.php faile, prašome perskaityti dokumentaciją.", "%1$s and %2$s" : "%1$s ir %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s ir %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s ir %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s ir %5$s", "PHP %s or higher is required." : "Reikalinga PHP %s arba aukštesnė.", "PHP with a version lower than %s is required." : "Reikalinga žemesnė nei %s PHP versija. ", "Following databases are supported: %s" : "Yra palaikomos šios duomenų bazės: %s", "The command line tool %s could not be found" : "Nepavyko rasti komandų eilutės įrankio %s", "The library %s is not available." : "Biblioteka %s nėra prieinama.", "Library %s with a version higher than %s is required - available version %s." : "Bibliotekos %s versija turi būti aukštesnė nei %s - turima versija %s.", "Library %s with a version lower than %s is required - available version %s." : "Bibliotekos %s versija turi būti žemesnė nei %s - turima versija %s.", "Following platforms are supported: %s" : "Yra palaikomos šios platformos: %s", "Server version %s or higher is required." : "Reikalinga %s arba aukštesnė serverio versija ", "Server version %s or lower is required." : "Reikalinga %s arba žemesnė serverio versija. ", "Unknown filetype" : "Nežinomas failo tipas", "Invalid image" : "Neteisingas paveikslas", "today" : "šiandien", "yesterday" : "vakar", "_%n day ago_::_%n days ago_" : ["prieš %n dieną","prieš %n dienas","prieš %n dienų"], "last month" : "praeitą mėnesį", "_%n month ago_::_%n months ago_" : ["prieš %n mėnesį","prieš %n mėnesius","prieš %n mėnesių"], "last year" : "praeitais metais", "_%n year ago_::_%n years ago_" : ["prieš %n metus","prieš %n metus","prieš %n metų"], "_%n hour ago_::_%n hours ago_" : ["prieš %n valandą","prieš %n valandas","prieš %n valandų"], "_%n minute ago_::_%n minutes ago_" : ["prieš %n minutę","prieš % minutes","prieš %n minučių"], "seconds ago" : "prieš keletą sekundžių", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulio, kurio id: %s, nėra. Prašome jį įjungti savo programėlių nustatymuose arba susisiekti su savo administratoriumi.", "File name is a reserved word" : "Failo pavadinimas negalimas, žodis rezervuotas", "File name contains at least one invalid character" : "Failo vardas sudarytas iš neleistinų simbolių", "File name is too long" : "Failo pavadinimas per ilgas", "Empty filename is not allowed" : "Tuščias failo pavadinimas nėra leidžiamas", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Programėlė \"%s\" negali būti įdiegta, kadangi negalima perskaityti appinfo failo.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Programėlė \"%s\" negali būti įdiegta, kadangi ji nėra suderinama su serverio versija.", "This is an automatically sent email, please do not reply." : "Tai yra automatinis pranešimas, prašome neatsakyti.", "Help" : "Pagalba", "Apps" : "Programėlės", "Settings" : "Nustatymai", "Log out" : "Atsijungti", "Users" : "Naudotojai", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Pagrindiniai nustatymai", "Sharing" : "Dalijimasis", "Security" : "Saugumas", "Encryption" : "Šifravimas", "Additional settings" : "Papildomi nustatymai", "Tips & tricks" : "Patarimai ir gudrybės", "Personal info" : "Asmeninė informacija", "Sync clients" : "Sinchronizavimo klientas", "Unlimited" : "Neribota", "__language_name__" : "Lietuvių", "Verifying" : "Tikrinimas", "Verifying …" : "Tikrinama...", "Verify" : "Patikrinti", "%s enter the database username and name." : "%s įrašykite duomenų bazės naudotojo vardą ir pavadinimą.", "%s enter the database username." : "%s įrašykite duomenų bazės naudotojo vardą.", "%s enter the database name." : "%s įrašykite duomenų bazės pavadinimą.", "%s you may not use dots in the database name" : "%s negalite naudoti taškų duombazės pavadinime", "Oracle connection could not be established" : "Nepavyko užmegzti Oracle ryšio", "Oracle username and/or password not valid" : "Neteisingas Oracle naudotojo vardas ir/arba slaptažodis", "PostgreSQL username and/or password not valid" : "Neteisingas PostgreSQL naudotojo vardas ir/arba slaptažodis", "You need to enter details of an existing account." : "Jūs turite suvesti egzistuojančios paskyros duomenis.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nėra palaikomas, %s neveiks tinkamai šioje platformoje. Naudodami prisiimate visą riziką !", "Set an admin username." : "Nustatyti administratoriaus naudotojo vardą.", "Set an admin password." : "Nustatyti administratoriaus slaptažodį.", "Can't create or write into the data directory %s" : "Negalima nuskaityti arba rašyti į duomenų katalogą. %s", "Invalid Federated Cloud ID" : "Netinkamas Centralizuoto Serverio ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "%s dalinimasis nepavyko, nes sistema nepalaiko šio duomenų tipo %i", "Sharing %s failed, because the file does not exist" : "%s dalinimasis nepavyko, nes failas neegzistuoja. ", "You are not allowed to share %s" : "Jums neleidžiama bendrinti %s", "Sharing %s failed, because you can not share with yourself" : "%s bendrinimas nepavyko, jūs negalite bendrinti su savimi pačiu.", "Sharing %s failed, because the user %s does not exist" : "%s bendrinimas nepavyko, nes naudotojas %s neegzistuoja", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s bendrinimas nepavyko, nes naudotojas %s nėra tos pačios grupės, kaip %s, narys.", "Sharing %s failed, because this item is already shared with %s" : "%s bendrinimas nepavyko, kadangi šis elementas jau yra bendrinamas su %s", "Sharing %s failed, because this item is already shared with user %s" : "%s bendrinimas nepavyko, kadangi šis elementas jau yra bendrinamas su naudotoju %s", "Sharing %s failed, because the group %s does not exist" : "%s bendrinimas nepavyko, nes grupė %s neegzistuoja", "Sharing %s failed, because %s is not a member of the group %s" : " %s bendrinimas nepavyko, nes %s nėra %s grupės narys.", "You need to provide a password to create a public link, only protected links are allowed" : "Viešoms nuorodoms būtinas slaptažodis, leidžiamos tik apsaugotos nuorodos.", "Sharing %s failed, because sharing with links is not allowed" : "Bendrinimas %s nepavyko, kadangi bendrinimas su nuorodomis yra neleidžiamas.", "Not allowed to create a federated share with the same user" : "Negalima dalintis su identišku naudotoju kitame serveryje", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s pasidalinimas nepavyko, neįmanoma rasti %s, tikėtina, kad serveris šiuo metu nepasiekiamas", "Share type %s is not valid for %s" : "Bendrinimo tipas %s netinka %s", "Cannot set expiration date. Expiration date is in the past" : "Nepavyko nustatyti galiojimo datos. Galiojimo data yra praėjęs laikas.", "Sharing failed, because the user %s is the original sharer" : "Bendrinimas nepavyko, nes naudotojas %s yra bendrintojas.", "Sharing %s failed, because resharing is not allowed" : "%s bendrinimas nepavyko, nes perskirstymas yra neleidžiamas.", "Can’t increase permissions of %s" : "Negalima pridėti papildomų %s leidimų", "Expiration date is in the past" : "Bendrinimo pabaigos data yra praėjęs laikas", "Can’t set expiration date more than %s days in the future" : "Negalima nustatyti galiojimo laiko ilgesnio nei %s dienos.", "%s shared »%s« with you" : "%s pasidalino »%s« su jumis", "%s shared »%s« with you." : "%s pasidalino »%s« su Jumis.", "Click the button below to open it." : "Norėdami atverti failą, spustelėkite mygtuką žemiau.", "Open »%s«" : "Atverti \"%s\"", "%s via %s" : "%s per %s", "The requested share does not exist anymore" : "Pageidaujamas bendrinimas daugiau neegzistuoja.", "Could not find category \"%s\"" : "Nepavyko rasti kategorijos „%s“", "Sunday" : "Sekmadienis", "Monday" : "Pirmadienis", "Tuesday" : "Antradienis", "Wednesday" : "Trečiadienis", "Thursday" : "Ketvirtadienis", "Friday" : "Penktadienis", "Saturday" : "Šeštadienis", "Sun." : "Sek.", "Mon." : "Pir.", "Tue." : "Ant.", "Wed." : "Tre.", "Thu." : "Ket.", "Fri." : "Pen.", "Sat." : "Šeš.", "Su" : "Sk", "Mo" : "Pr", "Tu" : "An", "We" : "Tr", "Th" : "Kt", "Fr" : "Pn", "Sa" : "Št", "January" : "Sausis", "February" : "Vasaris", "March" : "Kovas", "April" : "Balandis", "May" : "Gegužė", "June" : "Birželis", "July" : "Liepa", "August" : "Rugpjūtis", "September" : "Rugsėjis", "October" : "Spalis", "November" : "Lapkritis", "December" : "Gruodis", "Jan." : "Sau.", "Feb." : "Vas.", "Mar." : "Kov.", "Apr." : "Bal.", "May." : "Geg.", "Jun." : "Bir.", "Jul." : "Lie.", "Aug." : "Rgp.", "Sep." : "Rgs.", "Oct." : "Spl.", "Nov." : "Lap.", "Dec." : "Grd.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Naudotojo varde galima naudoti tik sekančius simbolius: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Privalo būti pateiktas tinkamas naudotojo vardas", "Username contains whitespace at the beginning or at the end" : "Naudotojo varde pradžioje ar pabaigoje yra tarpas", "Username must not consist of dots only" : "Naudotojo vardas negali būti sudarytas tik iš taškų.", "A valid password must be provided" : "Slaptažodis turi būti tinkamas", "The username is already being used" : "Naudotojo vardas jau yra naudojamas", "Could not create user" : "Nepavyko sukurti naudotojo", "User disabled" : "Naudotojas išjungtas", "Login canceled by app" : "Programėlė nutraukė prisijungimo procesą", "No app name specified" : "Nenurodytas programėlės pavadinimas", "App '%s' could not be installed!" : "Nepavyko įdiegti '%s' programėlės!", "a safe home for all your data" : "saugūs namai visiems jūsų duomenims", "File is currently busy, please try again later" : "Failas šiuo metu yra užimtas, prašome vėliau pabandyti dar kartą", "Can't read file" : "Nepavyksta perskaityti failo", "Application is not enabled" : "Programa neįjungta", "Authentication error" : "Tapatybės nustatymo klaida", "Token expired. Please reload page." : "Pasibaigė prieigos rakto galiojimas. Prašome įkelti puslapį iš naujo.", "Unknown user" : "Nežinomas naudotojas", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nėra įdiegtos duomenų bazių tvarkyklės (sqlite, mysql, or postgresql)", "Cannot write into \"config\" directory" : "Nepavyksta rašyti į \"config\" katalogą!", "Cannot write into \"apps\" directory" : "Nepavyksta įrašyti į \"apps\" katalogą", "Cannot create \"data\" directory" : "Nepavyksta sukurti katalogo \"data\"", "Please install one of these locales on your system and restart your webserver." : "Prašome įdiekite vieną šių lokalių savo sistemoje ir perkraukite žiniatinklio serverį.", "Please ask your server administrator to install the module." : "Kreipkitės į savo sistemos administratorių, kad jis įdiegtų modulį.", "PHP module %s not installed." : "PHP modulis %s neįdiegtas.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP nustatymas \"%s\" nenustatytas į \"%s\".", "To fix this issue update your libxml2 version and restart your web server." : "Atnaujinkite libxml2 versiją ir perkraukite žiniatinklio serverį, kad sutvarkytumėte šią problemą.", "PHP modules have been installed, but they are still listed as missing?" : "PHP moduliai yra įdiegti, bet jų vis tiek trūksta?", "Please ask your server administrator to restart the web server." : "Kreipkitės į savo sistemos administratorių, kad jis perkrautų žiniatinklio serverį.", "PostgreSQL >= 9 required" : "Reikalinga PostgreSQL >= 9", "Please upgrade your database version" : "Atnaujinkite duomenų bazės versiją.", "Your data directory is invalid" : "Neteisingas duomenų katalogas", "Storage unauthorized. %s" : "Saugykla nesankcionuota. %s", "Storage incomplete configuration. %s" : "Nepilna saugyklos konfigūracija. %s", "Storage connection error. %s" : "Saugyklos sujungimo ryšio klaida. %s", "Storage is temporarily not available" : "Saugykla yra laikinai neprieinama", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulio, kurio id: %s, nėra. Prašome jį įjungti savo programėlių nustatymuose arba susisiekti su savo administratoriumi.", "Server settings" : "Serverio nustatymai", "DB Error: \"%s\"" : "DB klaida: \"%s\"", "Offending command was: \"%s\"" : "Vykdyta komanda buvo: \"%s\"", "You need to enter either an existing account or the administrator." : "Turite prisijungti su egzistuojančia paskyra arba su administratoriumi.", "Offending command was: \"%s\", name: %s, password: %s" : "Vykdyta komanda buvo: \"%s\", name: %s, password: %s", "Cannot increase permissions of %s" : "Negalima pridėti papildomų %s leidimų", "Files can't be shared with delete permissions" : "Failai negali būti bendrinami su trynimo leidimu.", "Files can't be shared with create permissions" : "Failai negali būti bendrinami su sukūrimo leidimu.", "Personal" : "Asmeniniai", "Admin" : "Administravimas", "Cannot create \"data\" directory (%s)" : "Nepavyksta sukurti katalogo \"data\" (%s)", "Data directory (%s) is readable by other users" : "Duomenų katalogą (%s) skaito kiti naudotojai", "Data directory (%s) is invalid" : "Duomenų katalogas (%s) netinkamas." }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"); l10n/ast.js 0000604 00000045346 15247130447 0006462 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "¡Nun pue escribise nel direutoriu «config»!", "This can usually be fixed by giving the webserver write access to the config directory" : "Davezu esto pue iguase dándo-y al sirvidor web accesu d'escritura al direutoriu de configuración", "See %s" : "Mira %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Esto davezu íguase dando'l permisu d'escritura nel direutoriu de configuración al sirvidor web. Mira %s", "Sample configuration detected" : "Configuración d'amuesa detectada", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Detectose que la configuración d'amuesa copiose. Esto pue encaboxar la instalación y dexala ensín soporte. Llee la documentación enantes de facer cambéos en config.php", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "Education Edition" : "Edición educativa", "Enterprise bundle" : "Llote empresarial", "Social sharing bundle" : "Llote de compartición social", "PHP %s or higher is required." : "Necesítase PHP %s o superior", "PHP with a version lower than %s is required." : "Necesítase una versión PHP anterior a %s", "%sbit or higher PHP required." : "Necesítase PHP %sbit o superior", "Following databases are supported: %s" : "Les siguientes bases de datos tan sofitaes: %s", "The command line tool %s could not be found" : "La ferramienta línea de comandu %s nun pudo alcontrase", "The library %s is not available." : "La librería %s nun ta disponible", "Library %s with a version higher than %s is required - available version %s." : "Necesítase una librería %s con ua versión superior a %s - versión disponible %s.", "Library %s with a version lower than %s is required - available version %s." : "Necesítase una librería %s con una versión anterior a %s - versión disponible %s.", "Following platforms are supported: %s" : "Les siguientes plataformes tan sofitaes: %s", "Unknown filetype" : "Triba de ficheru desconocida", "Invalid image" : "Imaxe inválida", "Avatar image is not square" : "La imaxe del avatar nun ye cuadrada", "today" : "güei", "yesterday" : "ayeri", "_%n day ago_::_%n days ago_" : ["hai %n día","hai %n díes"], "last month" : "mes caberu", "_%n month ago_::_%n months ago_" : ["hai %n mes","hai %n meses"], "last year" : "añu caberu", "_%n year ago_::_%n years ago_" : ["hai %n añu","hai %n años"], "_%n hour ago_::_%n hours ago_" : ["hai %n hora","hai %n hores"], "_%n minute ago_::_%n minutes ago_" : ["hai %n minutu","hai %n minutos"], "seconds ago" : "hai segundos", "File name is a reserved word" : "El nome de ficheru ye una pallabra reservada", "File name contains at least one invalid character" : "El nome del ficheru contién polo menos un carácter non válidu", "File name is too long" : "El nome de ficheru ye demasiáu llargu", "Empty filename is not allowed" : "Nun s'almite un nome de ficheru baleru", "App \"%s\" cannot be installed because appinfo file cannot be read." : "L'aplicación \"%s\" nun puede instalase porque nun se llee'l ficheru appinfo.", "Help" : "Ayuda", "Apps" : "Aplicaciones", "Log out" : "Zarrar sesión", "Users" : "Usuarios", "APCu" : "APCu", "Basic settings" : "Axustes básicos", "Security" : "Seguranza", "Encryption" : "Cifráu", "Additional settings" : "Axustes adicionales", "Tips & tricks" : "Conseyos y trucos", "__language_name__" : "Asturianu", "%s enter the database username and name." : "%s introducir el nome d'usuariu y el nome de la base de datos .", "%s enter the database username." : "%s introducir l'usuariu de la base de datos.", "%s enter the database name." : "%s introducir nome de la base de datos.", "%s you may not use dots in the database name" : "%s nun pues usar puntos nel nome de la base de datos", "Oracle connection could not be established" : "Nun pudo afitase la conexón d'Oracle", "Oracle username and/or password not valid" : "Nome d'usuariu o contraseña d'Oracle non válidos", "PostgreSQL username and/or password not valid" : "Nome d'usuariu o contraseña PostgreSQL non válidos", "You need to enter details of an existing account." : "Precises introducir los detalles d'una cuenta esistente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nun ta sofitáu y %s nun furrulará afayadizamente nesta plataforma. ¡Úsalu baxo'l to riesgu!", "For the best results, please consider using a GNU/Linux server instead." : "Pa los meyores resultaos, por favor considera l'usu d'un sirvidor GNU/Linux nel so llugar.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Paez ser que la instancia %s ta executándose nun entornu de PHP 32 bits y el open_basedir configuróse en php.ini. Esto va dar llugar a problemes colos ficheros de más de 4 GB y nun ye nada recomendable.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor, desanicia la configuración open_basedir dientro la so php.ini o camude a PHP 64 bits.", "Set an admin username." : "Afitar nome d'usuariu p'almin", "Set an admin password." : "Afitar contraseña p'almin", "Can't create or write into the data directory %s" : "Nun pue crease o escribir dientro los datos del direutoriu %s", "Invalid Federated Cloud ID" : "ID non válida de ñube federada", "Sharing %s failed, because the backend does not allow shares from type %i" : "Compartir %s falló, por cuenta qu'el backend nun dexa acciones de tipu %i", "Sharing %s failed, because the file does not exist" : "Compartir %s falló, porque'l ficheru nun esiste", "You are not allowed to share %s" : "Nun tienes permisu pa compartir %s", "Sharing %s failed, because you can not share with yourself" : "Compartir %s falló, porque nun puede compartise contigo mesmu", "Sharing %s failed, because the user %s does not exist" : "Compartir %s falló, yá que l'usuariu %s nun esiste", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Compartir %s falló, yá que l'usuariu %s nun ye miembru de nengún de los grupos de los que ye miembru %s", "Sharing %s failed, because this item is already shared with %s" : "Compartir %s falló, porque esti elementu yá ta compartiéndose con %s", "Sharing %s failed, because this item is already shared with user %s" : "Compartir %s falló, porque esti elementu yá ta compartiéndose col usuariu %s", "Sharing %s failed, because the group %s does not exist" : "Compartir %s falló, porque'l grupu %s nun esiste", "Sharing %s failed, because %s is not a member of the group %s" : "Compartir %s falló, porque %s nun ye miembru del grupu %s", "You need to provide a password to create a public link, only protected links are allowed" : "Necesites apurrir una contraseña pa crear un enllaz públicu, namái tan permitíos los enllaces protexíos", "Sharing %s failed, because sharing with links is not allowed" : "Compartir %s falló, porque nun se permite compartir con enllaces", "Not allowed to create a federated share with the same user" : "Nun s'almite crear un recursu compartíu federáu col mesmu usuariu", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Compartir %s falló, nun pudo atopase %s, pue qu'el servidor nun seya anguaño algamable.", "Share type %s is not valid for %s" : "La triba de compartición %s nun ye válida pa %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Nun pue afitase la data de caducidá. Ficheros compartíos nun puen caducar dempués de %s de compartise", "Cannot set expiration date. Expiration date is in the past" : "Nun pue afitase la data d'espiración. La data d'espiración ta nel pasáu", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El motor compartíu %s tien d'implementar la interfaz OCP\\Share_Backend", "Sharing backend %s not found" : "Nun s'alcontró'l botón de compartición %s", "Sharing backend for %s not found" : "Nun s'alcontró'l botón de partición pa %s", "Sharing failed, because the user %s is the original sharer" : "Compartir falló, porque l'usuariu %s ye'l compartidor orixinal", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Compartir %s falló, porque los permisos perpasen los otorgaos a %s", "Sharing %s failed, because resharing is not allowed" : "Compartir %s falló, porque nun se permite la re-compartición", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Compartir %s falló porque'l motor compartíu pa %s podría nun atopar el so orixe", "Sharing %s failed, because the file could not be found in the file cache" : "Compartir %s falló, yá que'l ficheru nun pudo atopase na caché de ficheru", "Can’t increase permissions of %s" : "Nun pue aumentase los permisos de %s", "Files can’t be shared with delete permissions" : "Los ficheros nun puen compartise colos permisos de desaniciu", "Files can’t be shared with create permissions" : "Los ficheros nun puen compartise colos permisos de creación", "Expiration date is in the past" : "La data de caducidá ta nel pasáu.", "Can’t set expiration date more than %s days in the future" : "Nun pue afitase la data de caducidá más de %s díes nel futuru", "%s shared »%s« with you" : "%s compartió »%s« contigo", "%s via %s" : "%s via %s", "Could not find category \"%s\"" : "Nun pudo alcontrase la estaya \"%s.\"", "Sunday" : "Domingu", "Monday" : "Llunes", "Friday" : "Vienres", "Saturday" : "Sábadu", "Mon." : "Llu.", "Sat." : "Sáb.", "January" : "Xineru", "February" : "Febreru", "March" : "Marzu", "April" : "Abril", "May" : "Mayu", "June" : "Xunu", "July" : "Xunetu", "August" : "Agostu", "September" : "Setiembre", "October" : "Ochobre", "November" : "Payares", "December" : "Avientu", "Jan." : "Xin.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "May.", "Jun." : "Xun.", "Jul." : "Xnt.", "Sep." : "Set.", "Oct." : "Och.", "Nov." : "Pay.", "Dec." : "Avi.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Namái tan permitíos los siguientes caráuteres nun nome d'usuariu: \"a-z\", \"A-Z\", \"0-9\", y \"_.@-'\"", "A valid username must be provided" : "Tien d'apurrise un nome d'usuariu válidu", "Username contains whitespace at the beginning or at the end" : "El nome d'usuario contién espacios en blancu al entamu o al final", "Username must not consist of dots only" : "El nome d'usuariu nun pue tener puntos", "A valid password must be provided" : "Tien d'apurrise una contraseña válida", "The username is already being used" : "El nome d'usuariu yá ta usándose", "User disabled" : "Usuariu desactiváu", "Login canceled by app" : "Aniciar sesión canceláu pola aplicación", "No app name specified" : "Nun s'especificó nome de l'aplicación", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "L'aplicación \"%s\" nun puede instalase porque les siguientes dependencies nun se cumplen: %s", "a safe home for all your data" : "un llar seguru pa tolos tos datos", "File is currently busy, please try again later" : "Fichaeru ta ocupáu, por favor intentelo de nuevu más tarde", "Can't read file" : "Nun ye a lleese'l ficheru", "Application is not enabled" : "L'aplicación nun ta habilitada", "Authentication error" : "Fallu d'autenticación", "Token expired. Please reload page." : "Token caducáu. Recarga la páxina.", "Unknown user" : "Usuariu desconocíu", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nun hai controladores de bases de datos (sqlite, mysql, o postgresql)", "Cannot write into \"config\" directory" : "Nun pue escribise nel direutoriu \"config\"", "Cannot write into \"apps\" directory" : "Nun pue escribise nel direutoriu \"apps\"", "Setting locale to %s failed" : "Falló l'activación del idioma %s", "Please install one of these locales on your system and restart your webserver." : "Instala ún d'estos locales nel to sistema y reanicia'l sirvidor web", "Please ask your server administrator to install the module." : "Por favor, entrúga-y al to alministrador del sirvidor pa instalar el módulu.", "PHP module %s not installed." : "Nun ta instaláu'l módulu PHP %s", "PHP setting \"%s\" is not set to \"%s\"." : "La configuración de PHP \"%s\" nun s'afita \"%s\".", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload afita \"%s\" en llugar del valor esperáu \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Pa solucionar esti problema definíu <code>mbstring.func_overload</code>a <code>0</code> nel so php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 ríquese siquier. Anguaño ta instaláu %s.", "To fix this issue update your libxml2 version and restart your web server." : "Pa solucionar esti problema actualiza latso versión de libxml2 y reanicia'l to sirvidor web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ta aparentemente configuráu pa desaniciar bloques de documentos en llinia. Esto va facer que delles aplicaciones principales nun tean accesibles.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dablemente esto seya culpa d'un caché o acelerador, como por exemplu Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Instaláronse los módulos PHP, ¿pero tán entá llistaos como faltantes?", "Please ask your server administrator to restart the web server." : "Por favor, entruga al to alministrador pa reaniciar el sirvidor web.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 requeríu", "Please upgrade your database version" : "Por favor, anueva la versión de la to base de datos", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor, camuda los permisos a 0770 pa que'l direutoriu nun pueda llistase por otros usuarios.", "Check the value of \"datadirectory\" in your configuration" : "Comprobar el valor del \"datadirectory\" na so configuración", "Your data directory is invalid" : "El to direutoriu de datos nun ye válidu", "Could not obtain lock type %d on \"%s\"." : "Nun pudo facese'l bloquéu %d en \"%s\".", "Storage unauthorized. %s" : "Almacenamientu desautorizáu. %s", "Storage incomplete configuration. %s" : "Configuración d'almacenamientu incompleta. %s", "Storage connection error. %s" : "Fallu de conexón al almacenamientu. %s", "Storage is temporarily not available" : "L'almacenamientu ta temporalmente non disponible", "Storage connection timeout. %s" : "Tiempu escosao de conexón al almacenamientu. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Davezu esto pue iguase %sdándo-y al sirvidor web accesu d'escritura al direutoriu de configuración%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Nun esiste'l módulu con id: %s . Por favor, activalu na configuración d'aplicaciones o contauta col alministrador.", "Server settings" : "Axustes del sirvidor", "DB Error: \"%s\"" : "Fallu BD: \"%s\"", "Offending command was: \"%s\"" : "Comandu infractor: \"%s\"", "You need to enter either an existing account or the administrator." : "Tienes d'inxertar una cuenta esistente o la del alministrador.", "Offending command was: \"%s\", name: %s, password: %s" : "El comandu infractor foi: \"%s\", nome: %s, contraseña: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Falló dar permisos a %s, porque los permisos son mayores que los otorgaos a %s", "Setting permissions for %s failed, because the item was not found" : "Falló dar permisos a %s, porque l'elementu nun s'atopó", "Cannot clear expiration date. Shares are required to have an expiration date." : "Non puede desaniciar la fecha de caducidá. Compartir obliga a tener una fecha de caducidá.", "Cannot increase permissions of %s" : "Nun se pueden aumentar los permisos de %s", "Files can't be shared with delete permissions" : "Los ficheros nun pueden compartise con permisos desaniciaos", "Files can't be shared with create permissions" : "Los ficheros nun pueden compartise con crear permisos", "Cannot set expiration date more than %s days in the future" : "Nun pue afitase la data d'espiración más que %s díes nel futuru", "Personal" : "Personal", "Admin" : "Almin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Esto pue iguase %sdando permisos d'escritura al sirvidor Web nel direutoriu%s d'apps o deshabilitando la tienda d'apps nel ficheru de configuración.", "Cannot create \"data\" directory (%s)" : "Nun pue crease'l direutoriu \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Esto pue iguase davezu <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">dándo-y accesu d'escritura al direutoriu raigañu</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Davezu los permisos puen iguase %sdándo-y al sirvidor web accesu d'escritura al direutoriu raigañu%s.", "Data directory (%s) is readable by other users" : "El direutoriu de datos (%s) ye llexible por otros usuarios", "Data directory (%s) must be an absolute path" : "El directoriu de datos (%s) ha de ser una ruta absoluta", "Data directory (%s) is invalid" : "Ye inválidu'l direutoriu de datos (%s)", "Please check that the data directory contains a file \".ocdata\" in its root." : "Verifica que'l direutoriu de datos contién un ficheru \".ocdata\" nel direutoriu raigañu." }, "nplurals=2; plural=(n != 1);"); l10n/sk.json 0000604 00000045455 15247130447 0006646 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Nie je možné zapisovat do priečinka \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "To je zvyčajne možné opraviť tým, že udelíte webovému serveru oprávnenie na zápis do priečinka s konfiguráciou.", "See %s" : "Pozri %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Súbory aplikácie %$1s nebolo možné úspešne nahradiť. Uistite sa, že verzia je kompatibilná s verziou servera.", "Sample configuration detected" : "Detekovaná bola vzorová konfigurácia", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Zistilo sa, že konfigurácia bola skopírovaná zo vzorových súborov. Takáto konfigurácia nie je podporovaná a môže poškodiť vašu inštaláciu. Prečítajte si dokumentáciu pred vykonaním zmien v config.php", "%1$s and %2$s" : "%1$s a %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s a %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s a %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s a %5$s", "PHP %s or higher is required." : "Požadovaná verzia PHP %s alebo vyššia.", "PHP with a version lower than %s is required." : "PHP je vyžadované vo vyššej verzii ako %s.", "%sbit or higher PHP required." : "%sbit alebo vyššie PHP je vyžadované.", "Following databases are supported: %s" : "Podporované sú tieto databázy: %s", "The command line tool %s could not be found" : "Nástroj príkazového riadka %s nebol nájdený", "The library %s is not available." : "Knižnica %s je nedostupná.", "Library %s with a version higher than %s is required - available version %s." : "Požadovaná je knižnica %s vo vyššej verzii ako %s - dostupná verzia %s.", "Library %s with a version lower than %s is required - available version %s." : "Požadovaná je knižnica %s v nižšej verzii ako %s - dostupná verzia %s.", "Following platforms are supported: %s" : "Podporované sú nasledovné systémy: %s", "Server version %s or higher is required." : "Je vyžadovaná verzia servera %s alebo vyššia.", "Server version %s or lower is required." : "Je vyžadovaná verzia servera %s alebo nižšia.", "Unknown filetype" : "Neznámy typ súboru", "Invalid image" : "Chybný obrázok", "Avatar image is not square" : "Obrázok avatara nie je štvorcový", "today" : "dnes", "yesterday" : "včera", "_%n day ago_::_%n days ago_" : ["včera","pred %n dňami","pred %n dňami"], "last month" : "minulý mesiac", "_%n month ago_::_%n months ago_" : ["pred %n mesiacom","pred %n mesiacmi","pred %n mesiacmi"], "last year" : "minulý rok", "_%n year ago_::_%n years ago_" : ["vlani","pred %n rokmi","pred %n rokmi"], "_%n hour ago_::_%n hours ago_" : ["pred %n hodinou","pred %n hodinami","pred %n hodinami"], "_%n minute ago_::_%n minutes ago_" : ["pred %n minútou","pred %n minútami","pred %n minútami"], "seconds ago" : "pred sekundami", "File name is a reserved word" : "Názov súboru je rezervované slovo.", "File name contains at least one invalid character" : "Názov súboru obsahuje nepovolené znaky.", "File name is too long" : "Meno súboru je veľmi dlhé.", "Dot files are not allowed" : "Názov súboru začínajúci bodkou nie je povolený.", "Empty filename is not allowed" : "Prázdny názov súboru nie je povolený", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Aplikáciu \"%s\" nie je možné nainštalovať, lebo nebolo možné načítať súbor s informáciami o aplikácií.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikácia \"%s\" nie je kompatibilná s verziou servera, preto nemôže byť nainštalovaná.", "Help" : "Pomoc", "Apps" : "Aplikácie", "Users" : "Používatelia", "APCu" : "APCu", "Redis" : "Redis", "Sharing" : "Sprístupnenie", "Encryption" : "Šifrovanie", "Additional settings" : "Ďalšie nastavenia", "Tips & tricks" : "Tipy a triky", "%s enter the database username." : "Zadajte používateľské meno %s databázy.", "%s enter the database name." : "Zadajte názov databázy pre %s databázy.", "%s you may not use dots in the database name" : "V názve databázy %s nemôžete používať bodky", "Oracle connection could not be established" : "Nie je možné pripojiť sa k Oracle", "Oracle username and/or password not valid" : "Používateľské meno a/alebo heslo pre Oracle databázu je neplatné", "PostgreSQL username and/or password not valid" : "Používateľské meno a/alebo heslo pre PostgreSQL databázu je neplatné", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie je podporovaný a %s nebude správne fungovať na tejto platforme. Použite ho na vlastné riziko!", "For the best results, please consider using a GNU/Linux server instead." : "Pre dosiahnutie najlepších výsledkov, prosím zvážte použitie GNU/Linux servera.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Zdá sa, že táto inštancia %s beží v 32-bitovom prostredí PHP a v php.ini bola nastavená voľba open_basedir. To bude zdrojom problémov so súbormi väčšími ako 4GB a dôrazne sa neodporúča.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Prosím, odstráňte nastavenie open_basedir vo vašom php.ini alebo prejdite na 64-bit PHP.", "Set an admin username." : "Zadajte používateľské meno administrátora.", "Set an admin password." : "Zadajte heslo administrátora.", "Can't create or write into the data directory %s" : "Nemožno vytvoriť alebo zapisovať do priečinka dát %s", "Invalid Federated Cloud ID" : "Neplatné združené Cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Sprístupnenie %s zlyhalo, backend nepodporuje typ sprístupnenia %i", "Sharing %s failed, because the file does not exist" : "Nie je možné sprístupniť %s, súbor neexistuje", "You are not allowed to share %s" : "Nemôžete sprístupniť %s", "Sharing %s failed, because you can not share with yourself" : "Sprístupnenie %s zlyhalo, nieje možné sprístupniť obsah so sebou samým", "Sharing %s failed, because the user %s does not exist" : "Sprístupnenie %s zlyhalo, používateľ %s neexistuje", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Sprístupnenie %s zlyhalo, používateľ %s nie je členom žiadnej skupiny spoločnej s používateľom %s", "Sharing %s failed, because this item is already shared with %s" : "Sprístupnenie %s zlyhalo, pretože táto položka už je prístupná pre %s", "Sharing %s failed, because this item is already shared with user %s" : "Sprístupnenie %s zlyhalo, táto položka už je používateľovi %s prístupná", "Sharing %s failed, because the group %s does not exist" : "Sprístupnenie %s zlyhalo, skupina %s neexistuje", "Sharing %s failed, because %s is not a member of the group %s" : "Sprístupnenie %s zlyhalo, %s nie je členom skupiny %s", "You need to provide a password to create a public link, only protected links are allowed" : "Musíte zadať heslo ak chcete vytvoriť verejný odkaz, lebo iba odkazy chránené heslom sú povolené", "Sharing %s failed, because sharing with links is not allowed" : "%s nie je možné sprístupniť, sprístupnenie prostredníctvom odkazu nie je povolené", "Not allowed to create a federated share with the same user" : "Nie je možné vytvoriť združené sprístupnenie so sebou samým", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Sprístupňovanie %s zlyhalo, nepodarilo sa nájsť %s, možno je server dočasne nedostupný.", "Share type %s is not valid for %s" : "Typ sprístupnenia %s nie je možný pre %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Sprístupnenie nemôže byť ukončené skôr, ako po %s dňoch.", "Cannot set expiration date. Expiration date is in the past" : "Nie je možné nastaviť dátum konca platnosti. Dátum konca platnosti je v minulosti.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Backend pre sprístupnenie %s musí implementovať rozhranie OCP\\Share_Backend", "Sharing backend %s not found" : "Backend sprístupnenia %s nebol nájdený", "Sharing backend for %s not found" : "Backend sprístupnenia pre %s nebol nájdený", "Sharing failed, because the user %s is the original sharer" : "Sprístupnenie zlyhalo, pretože používateľ %s je pôvodný spoločný používateľ", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Sprístupnenie %s zlyhalo, pretože povolenia prekračujú povolenia udelené %s", "Sharing %s failed, because resharing is not allowed" : "Nie je možné sprístupniť %s ďalším osobám", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Sprístupnenie %s zlyhalo, backend nenašiel zdrojový %s", "Sharing %s failed, because the file could not be found in the file cache" : "Sprístupnenie %s zlyhalo, pretože súbor sa nenachádza vo vyrovnávacej pamäti súborov", "Expiration date is in the past" : "Dátum konca platnosti je v minulosti", "%s shared »%s« with you" : "%s vám sprístupnil »%s«", "%s via %s" : "%s cez %s", "Could not find category \"%s\"" : "Nemožno nájsť danú kategóriu \"%s\"", "Sunday" : "Nedeľa", "Monday" : "Pondelok", "Tuesday" : "Utorok", "Wednesday" : "Streda", "Thursday" : "Štvrtok", "Friday" : "Piatok", "Saturday" : "Sobota", "Sun." : "Ned.", "Mon." : "Pon.", "Tue." : "Uto.", "Wed." : "Str.", "Thu." : "Štv.", "Fri." : "Pia.", "Sat." : "Sob.", "Su" : "Ne", "Mo" : "Po", "Tu" : "Ut", "We" : "St", "Th" : "Št", "Fr" : "Pi", "Sa" : "So", "January" : "Január", "February" : "Február", "March" : "Marec", "April" : "Apríl", "May" : "Máj", "June" : "Jún", "July" : "Júl", "August" : "August", "September" : "September", "October" : "Október", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Máj.", "Jun." : "Jún.", "Jul." : "Júl.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "V mene používateľa je možné použiť iba nasledovné znaky: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Musíte zadať platné používateľské meno", "Username contains whitespace at the beginning or at the end" : "Meno používateľa obsahuje na začiatku, alebo na konci medzeru", "A valid password must be provided" : "Musíte zadať platné heslo", "The username is already being used" : "Meno používateľa je už použité", "User disabled" : "Používateľ zakázaný", "Login canceled by app" : "Prihlásenie bolo zrušené aplikáciou", "No app name specified" : "Nešpecifikované meno aplikácie", "App '%s' could not be installed!" : "Aplikáciu '%s' nebolo možné nainštalovať!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Aplikáciu \"%s\" nie je možné inštalovať, pretože nie sú splnené nasledovné závislosti: %s", "a safe home for all your data" : "bezpečný domov pre všetky vaše dáta", "File is currently busy, please try again later" : "Súbor sa práve používa, skúste prosím neskôr", "Can't read file" : "Nemožno čítať súbor.", "Application is not enabled" : "Aplikácia nie je zapnutá", "Authentication error" : "Chyba autentifikácie", "Token expired. Please reload page." : "Token vypršal. Obnovte, prosím, stránku.", "Unknown user" : "Neznámy používateľ", "No database drivers (sqlite, mysql, or postgresql) installed." : "Ovládače databázy (sqlite, mysql, alebo postgresql) nie sú nainštalované.", "Cannot write into \"config\" directory" : "Nie je možné zapisovať do priečinka \"config\"", "Cannot write into \"apps\" directory" : "Nie je možné zapisovať do priečinka \"apps\"", "Setting locale to %s failed" : "Nastavenie locale na %s zlyhalo", "Please install one of these locales on your system and restart your webserver." : "Prosím, nainštalujte si aspoň jeden z týchto jazykov so svojho systému a reštartujte webserver.", "Please ask your server administrator to install the module." : "Prosím, požiadajte administrátora vášho servera o inštaláciu modulu.", "PHP module %s not installed." : "PHP modul %s nie je nainštalovaný.", "PHP setting \"%s\" is not set to \"%s\"." : "Voľba PHP „%s“ nie je nastavená na „%s“.", "Adjusting this setting in php.ini will make Nextcloud run again" : "Použitím týchto nastavení v php.ini dovolí Nextcloudu sa znova spustiť", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload je nastavený na \"%s\", namiesto predpokladanej hodnoty \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Oprava problému spočíva v nastavení <code>mbstring.func_overload</code> na <code>0</code> vo vašom php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Vyžadovaná verzia libxml2 je 2.7.0 a vyššia. Momentálne je nainštalovaná verzia %s.", "To fix this issue update your libxml2 version and restart your web server." : "Pre vyriešenie tohto problému aktualizujte prosím verziu libxml2 a reštartujte webový server.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP je zjavne nastavené, aby odstraňovalo bloky vloženej dokumentácie. To zneprístupní niekoľko základných aplikácií.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "To je pravdepodobne spôsobené cache/akcelerátorom ako napr. Zend OPcache alebo eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP moduly boli nainštalované, ale stále sa tvária, že chýbajú?", "Please ask your server administrator to restart the web server." : "Prosím, požiadajte administrátora vášho servera o reštartovanie webového servera.", "PostgreSQL >= 9 required" : "Vyžadované PostgreSQL >= 9", "Please upgrade your database version" : "Prosím, aktualizujte verziu svojej databázy", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Prosím, zmeňte oprávnenia na 0770, aby tento priečinok nemohli ostatní používatelia otvoriť.", "Check the value of \"datadirectory\" in your configuration" : "Skontrolujte hodnotu \"datadirectory\" vo vašej konfigurácii", "Could not obtain lock type %d on \"%s\"." : "Nepodarilo sa získať zámok typu %d na „%s“.", "Storage connection error. %s" : "Chyba pripojenia k úložisku. %s", "Storage is temporarily not available" : "Úložisko je dočasne nedostupné", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "To je zvyčajne možné opraviť tým, že %s udelíte webovému serveru oprávnenie na zápis k adresáru s konfiguráciou%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul s ID: %s neexistuje. Povoľte ho prosím vo vašom nastavení aplikácií alebo konaktujte správcu.", "Server settings" : "Nastavenia servera", "DB Error: \"%s\"" : "Chyba DB: \"%s\"", "Offending command was: \"%s\"" : "Podozrivý príkaz bol: \"%s\"", "You need to enter either an existing account or the administrator." : "Musíte zadať jestvujúci účet alebo administrátora.", "Offending command was: \"%s\", name: %s, password: %s" : "Podozrivý príkaz bol: \"%s\", meno: %s, heslo: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Nastavenie povolení pre %s zlyhalo, pretože povolenia prekračujú povolenia udelené %s", "Setting permissions for %s failed, because the item was not found" : "Nastavenie povolení pre %s zlyhalo, pretože položka sa nenašla", "Cannot clear expiration date. Shares are required to have an expiration date." : "Nemožno vymazať čas expirácie. Pri sprístupnení je čas exspirácie vyžadovaný.", "Cannot set expiration date more than %s days in the future" : "Nie je možné nastaviť dátum konca platnosti viac ako %s dní v budúcnosti", "Personal" : "Osobné", "Admin" : "Administrátor", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Toto je zvyčajne možné opraviť tým, že %s udelíte webovému serveru oprávnenie na zápis do priečinka aplikácií %s alebo vypnete obchod s aplikáciami v konfiguračnom súbore.", "Cannot create \"data\" directory (%s)" : "Nie je možné vytvoriť priečinok \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "To je zvyčajne možné opraviť tým <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">že udelíte webovému serveru oprávnenie na zápis do koreňového priečinka</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Oprávnenia je zvyčajne možné opraviť tým, že %sudelíte webovému serveru oprávnenie na zápis do koreňového priečinka%s.", "Data directory (%s) is readable by other users" : "Priečinok dát (%s) je prístupný na čítanie ostatným používateľom", "Data directory (%s) must be an absolute path" : "Priečinok dát (%s) musí byť zadaný ako absolútna cesta", "Data directory (%s) is invalid" : "Priečinok dát (%s) je neplatný", "Please check that the data directory contains a file \".ocdata\" in its root." : "Prosím, skontrolujte, či priečinok dát obsahuje súbor \".ocdata\"." },"pluralForm" :"nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" } l10n/nl.json 0000604 00000054233 15247130447 0006634 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Kan niet schrijven naar de \"config\" directory", "This can usually be fixed by giving the webserver write access to the config directory" : "Dit kan opgelost worden door de config map op de webserver schrijfrechten te geven", "See %s" : "Zie %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dit kan opgelost worden door de config map op de webserver schrijf rechten te geven. See %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "De bestanden van de app %$1s zijn niet correct vervangen. Zorg ervoor dat de app versie compatibel is met de server.", "Sample configuration detected" : "Voorbeeld configuratie gevonden", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Er is gedetecteerd dat de voorbeeld configuratie is gekopieerd. Dit kan je installatie beschadigen en wordt dan ook niet ondersteund. Lees de documentatie voordat je wijzigingen aan config.php doorvoert", "%1$s and %2$s" : "%1$s en %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s en %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s en %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s en %5$s", "Education Edition" : "Onderwijs Editie", "Enterprise bundle" : "Zakelijke bundel", "Groupware bundle" : "Groupware bundel", "Social sharing bundle" : "Sociaal delen bundel", "PHP %s or higher is required." : "PHP %s of hoger vereist.", "PHP with a version lower than %s is required." : "PHP met een versie lager dan %s is vereist.", "%sbit or higher PHP required." : "%sbit of hogere PHP versie vereist.", "Following databases are supported: %s" : "De volgende databases worden ondersteund: %s", "The command line tool %s could not be found" : "Commandoregel tool %s is niet gevonden", "The library %s is not available." : "Library %s is niet beschikbaar.", "Library %s with a version higher than %s is required - available version %s." : "Library %s met een versienummer hoger dan %s is vereist - beschikbare versie %s.", "Library %s with a version lower than %s is required - available version %s." : "Library %s met een versienummer lager dan %s is vereist - beschikbare versie %s.", "Following platforms are supported: %s" : "De volgende platformen worden ondersteund: %s", "Server version %s or higher is required." : "Serverversie %s of hoger vereist.", "Server version %s or lower is required." : "Serverversie %s of lager vereist.", "Unknown filetype" : "Onbekend bestandsformaat", "Invalid image" : "Ongeldige afbeelding", "Avatar image is not square" : "Avatar afbeelding is niet vierkant", "today" : "vandaag", "yesterday" : "gisteren", "_%n day ago_::_%n days ago_" : ["%n dag geleden","%n dagen geleden"], "last month" : "vorige maand", "_%n month ago_::_%n months ago_" : ["%n maand geleden","%n maanden geleden"], "last year" : "vorig jaar", "_%n year ago_::_%n years ago_" : ["%n jaar geleden","%n jaren geleden"], "_%n hour ago_::_%n hours ago_" : ["%n uur geleden","%n uren geleden"], "_%n minute ago_::_%n minutes ago_" : ["%n minuut geleden","%n minuten geleden"], "seconds ago" : "seconden geleden", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module met ID: %s bestaat niet. Schakel die in binnen de app-instellingen of neem contact op met je beheerder.", "File name is a reserved word" : "Bestandsnaam is een gereserveerd woord", "File name contains at least one invalid character" : "De bestandsnaam bevat in ieder geval één verboden teken", "File name is too long" : "De bestandsnaam is te lang", "Dot files are not allowed" : "Punt bestanden zijn niet toegestaan", "Empty filename is not allowed" : "Een lege bestandsnaam is niet toegestaan", "App \"%s\" cannot be installed because appinfo file cannot be read." : "App \"%s\" kan niet worden geïnstalleerd, omdat het app info bestand niet gelezen kan worden.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "App \"%s\" kan niet worden geïnstalleerd, omdat deze niet compatible is met deze versie van de server.", "This is an automatically sent email, please do not reply." : "Dit is een automatisch gegenereerde e-mail, dus reageren is niet mogelijk.", "Help" : "Help", "Apps" : "Apps", "Settings" : "Instellingen", "Log out" : "Uitloggen", "Users" : "Gebruikers", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Basis instellingen", "Sharing" : "Delen", "Security" : "Beveiliging", "Encryption" : "Versleuteling", "Additional settings" : "Aanvullende instellingen", "Tips & tricks" : "Tips & trucs", "Personal info" : "Persoonlijke informatie", "Sync clients" : "Synchronisatie clients", "Unlimited" : "Ongelimiteerd", "__language_name__" : "Nederlands", "Verifying" : "Verifiëren", "Verifying …" : "Verifiëren...", "Verify" : "Verifieer", "%s enter the database username and name." : "%s voer de database gebruikersnaam en naam in .", "%s enter the database username." : "%s voer de database gebruikersnaam in.", "%s enter the database name." : "%s voer de databasenaam in.", "%s you may not use dots in the database name" : "%s er mogen geen punten in de databasenaam voorkomen", "Oracle connection could not be established" : "Er kon geen verbinding met Oracle worden gemaakt.", "Oracle username and/or password not valid" : "Oracle gebruikersnaam en/of wachtwoord ongeldig", "PostgreSQL username and/or password not valid" : "PostgreSQL gebruikersnaam en/of wachtwoord ongeldig", "You need to enter details of an existing account." : "Geef de details van een bestaand account op.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OSX wordt niet ondersteund en %s zal niet goed werken op dit platform. Gebruik het op eigen risico!", "For the best results, please consider using a GNU/Linux server instead." : "Voor het beste resultaat adviseren wij het gebruik van een GNU/Linux server.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Het lijkt erop dat deze %s versie draait in een 32 bits PHP omgeving en dat open_basedir is geconfigureerd in php.ini. Dat zal leiden tot problemen met bestanden groter dan 4 GB en wordt dus sterk afgeraden.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Verwijder de open_basedir instelling in php.ini of schakel over op de 64bit PHP.", "Set an admin username." : "Stel de gebruikersnaam van de beheerder in.", "Set an admin password." : "Stel een beheerders wachtwoord in.", "Can't create or write into the data directory %s" : "Kan niets creëren of wegschrijven in de datadirectory %s", "Invalid Federated Cloud ID" : "Ongeldige gefedereerde Cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Delen van %s is mislukt, omdat de share-backend het niet toestaat om type %i te delen", "Sharing %s failed, because the file does not exist" : "Delen van %s is mislukt, omdat het bestand niet bestaat", "You are not allowed to share %s" : "Je bent niet bevoegd om %s te delen", "Sharing %s failed, because you can not share with yourself" : "Delen van %s is mislukt, omdat je niet met jezelf kan delen", "Sharing %s failed, because the user %s does not exist" : "Delen van %s is mislukt, omdat gebruiker %s niet bestaat", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Delen van %s is mislukt, omdat gebruiker %s geen lid is van een groep waar %s lid van is", "Sharing %s failed, because this item is already shared with %s" : "Delen van %s is mislukt, omdat het object al wordt gedeeld met %s", "Sharing %s failed, because this item is already shared with user %s" : "Delen van %s is mislukt, omdat het object al wordt gedeeld met gebruiker %s", "Sharing %s failed, because the group %s does not exist" : "Delen van %s is mislukt, omdat de groep %s niet bestaat", "Sharing %s failed, because %s is not a member of the group %s" : "Delen van %s is mislukt, omdat %s geen lid is van groep %s", "You need to provide a password to create a public link, only protected links are allowed" : "Je moet een wachtwoord opgeven om een openbare koppeling te maken, alleen wachtwoord beveiligde links zijn toegestaan", "Sharing %s failed, because sharing with links is not allowed" : "Delen van %s is mislukt, omdat het delen doormiddel van een een link niet is toegestaan", "Not allowed to create a federated share with the same user" : "Het is niet toegestaan om een gefedereerd gedeelde folder te maken met dezelfde gebruiker.", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Delen van %s mislukt, kon %s niet vinden, misschien is de server tijdelijk niet bereikbaar.", "Share type %s is not valid for %s" : "Delen van type %s is niet geldig voor %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Een vervaldatum kan niet worden ingesteld. Gedeelde folders kunnen niet vervallen na %s ", "Cannot set expiration date. Expiration date is in the past" : "Kon vervaldatum niet instellen. De vervaldatum ligt in het verleden", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "De gedeelde achtergrond %s moet de OCP\\Share_Backend interface implementeren", "Sharing backend %s not found" : "De gedeelde backend %s is niet gevonden", "Sharing backend for %s not found" : "De gedeelde backend voor %s is niet gevonden", "Sharing failed, because the user %s is the original sharer" : "Delen mislukt, omdat gebruiker %s de originele deler is", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Delen van %s is mislukt, omdat de rechten toegekend aan %s overschreden zijn.", "Sharing %s failed, because resharing is not allowed" : "Delen van %s is mislukt, omdat her-delen niet is toegestaan", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Delen van %s is mislukt, omdat de gedeelde backend voor %s de bron niet kon vinden", "Sharing %s failed, because the file could not be found in the file cache" : "Delen van %s is mislukt, omdat het bestand niet in de bestand cache kon worden gevonden", "Can’t increase permissions of %s" : "Kan niet meer rechten geven aan %s", "Files can’t be shared with delete permissions" : "Bestanden kunnen niet worden gedeeld met verwijder permissies", "Files can’t be shared with create permissions" : "Bestanden kunnen niet worden gedeeld met 'creëer' permissies", "Expiration date is in the past" : "De vervaldatum ligt in het verleden", "Can’t set expiration date more than %s days in the future" : "Kan de vervaldatum niet meer dan %s dagen in de toekomst instellen", "%s shared »%s« with you" : "%s deelde »%s« met jou", "%s shared »%s« with you." : "%s deelde »%s« met jou.", "Click the button below to open it." : "Klik de onderstaande button om te openen.", "Open »%s«" : "Open »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "De toegang tot de gedeelde folder bestaat niet meer", "Could not find category \"%s\"" : "Kan categorie \"%s\" niet vinden", "Sunday" : "Zondag", "Monday" : "Maandag", "Tuesday" : "Dinsdag", "Wednesday" : "Woensdag", "Thursday" : "Donderdag", "Friday" : "Vrijdag", "Saturday" : "Zaterdag", "Sun." : "Zo.", "Mon." : "Ma.", "Tue." : "Di.", "Wed." : "Wo.", "Thu." : "Do.", "Fri." : "Vr.", "Sat." : "Za.", "Su" : "Zo", "Mo" : "Ma", "Tu" : "Di", "We" : "Wo", "Th" : "Do", "Fr" : "Vr", "Sa" : "Za", "January" : "Januari", "February" : "Februari", "March" : "Maart", "April" : "April", "May" : "Mei", "June" : "Juni", "July" : "Juli", "August" : "Augustus", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mrt.", "Apr." : "Apr.", "May." : "Mei", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Alleen de volgende tekens zijn toegestaan in een gebruikersnaam: \"a-z\", \"A-Z\", \"0-9\", en \"_.@-\"", "A valid username must be provided" : "Er moet een geldige gebruikersnaam worden opgegeven", "Username contains whitespace at the beginning or at the end" : "De gebruikersnaam bevat spaties aan het begin of aan het eind", "Username must not consist of dots only" : "De gebruikersnaam mag niet uit alleen punten bestaan", "A valid password must be provided" : "Er moet een geldig wachtwoord worden opgegeven", "The username is already being used" : "De gebruikersnaam bestaat al", "Could not create user" : "Kan gebruiker niet aanmaken.", "User disabled" : "Gebruiker geblokkeerd", "Login canceled by app" : "Inloggen geannuleerd door app", "No app name specified" : "Geen app naam opgegeven.", "App '%s' could not be installed!" : "App '%s' kan niet worden geïnstalleerd!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "App \"%s\" kan niet worden geïnstalleerd, omdat de volgende afhankelijkheden nodig zijn: %s", "a safe home for all your data" : "een veilige plek voor al je gegevens", "File is currently busy, please try again later" : "Bestandsverwerking bezig, probeer het later opnieuw", "Can't read file" : "Kan bestand niet lezen", "Application is not enabled" : "De applicatie is niet ingeschakeld", "Authentication error" : "Authenticatie fout", "Token expired. Please reload page." : "Token verlopen. Herlaad de pagina.", "Unknown user" : "Onbekende gebruiker", "No database drivers (sqlite, mysql, or postgresql) installed." : "Geen database drivers (sqlite, mysql of postgres) geïnstalleerd.", "Cannot write into \"config\" directory" : "Kan niet schrijven naar de \"config\" directory", "Cannot write into \"apps\" directory" : "Kan niet schrijven naar de \"apps\" directory", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Dit kan hersteld worden door de app map schrijf rechten te geven iin de webserver of schakel de appstore uit bij het config bestand. Zie %s", "Cannot create \"data\" directory" : "\"data\" map kan niet worden aangemaakt", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dit kan hersteld worden door de root map schrijf rechten te geven op de webserver. Zie %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Rechten kunnen worden hersteld door de root map op de webserver schrijf toegang te geven. Zie %s.", "Setting locale to %s failed" : "Instellen taal op %s mislukte", "Please install one of these locales on your system and restart your webserver." : "Installeer één van de talen op je systeem en herstart je webserver.", "Please ask your server administrator to install the module." : "Vraag je beheerder om de module te installeren.", "PHP module %s not installed." : "PHP module %s niet geïnstalleerd.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP instelling \"%s\" staat niet op \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Het aanpassen van deze instelling in php.ini zorgt ervoor dat Nextcloud weer start", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload is ingesteld op \"%s\" in plaats van de verwachte waarde \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Om dit probleem op te lossen stel je in php.ini <code>mbstring.func_overload</code> in op <code>0</code>", "libxml2 2.7.0 is at least required. Currently %s is installed." : "De minimale versie van libxml2 versie is 2.7.0. Momenteel is versie%s geïnstalleerd.", "To fix this issue update your libxml2 version and restart your web server." : "Om dit probleem op te lossen, moet je de libxml2 versie bijwerken en je webserver herstarten.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP is nu zo ingesteld dat 'inline doc blocks' worden gestript. Hierdoor worden verschillende hoofd modules onbruikbaar.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dit wordt vermoedelijk veroorzaakt door een cache/accelerator, zoals Zend OPcache of eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP modules zijn geïnstalleerd, maar ze worden nog steeds als ontbrekend aangegeven?", "Please ask your server administrator to restart the web server." : "Vraag je beheerder de webserver opnieuw te starten.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 is vereist", "Please upgrade your database version" : "Werk je database versie bij", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Wijzig de permissie in 0770 zodat de directory niet door andere gebruikers bekeken kan worden.", "Your data directory is readable by other users" : "Je data map is leesbaar voor andere gebruikers", "Your data directory must be an absolute path" : "Je data map moet een absolute bestandslocatie hebben", "Check the value of \"datadirectory\" in your configuration" : "Controleer de waarde van \"datadirectory\" in je configuratie", "Your data directory is invalid" : "Je data folder is ongeldig", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Zorg dat er een bestand genaamd \".ocdata\" in de hoofddirectory aanwezig is.", "Could not obtain lock type %d on \"%s\"." : "Kon geen lock type %d krijgen op \"%s\".", "Storage unauthorized. %s" : "Opslag niet toegestaan. %s", "Storage incomplete configuration. %s" : "Incomplete opslag configuratie. %s", "Storage connection error. %s" : "Opslag verbindingsfout. %s", "Storage is temporarily not available" : "Opslag is tijdelijk niet beschikbaar", "Storage connection timeout. %s" : "Opslag verbinding time-out. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Dit kan hersteld worden door de webserver %sschrijfrechten te geven op de configuratie directory%s", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module met id: %s bestaat niet. Activeer het in je apps instellingen, of neem contact op met je beheerder.", "Server settings" : "Server instellingen", "DB Error: \"%s\"" : "DB Fout: \"%s\"", "Offending command was: \"%s\"" : "Onjuiste commande was: \"%s\"", "You need to enter either an existing account or the administrator." : "Geef een bestaand account op of het beheerdersaccount.", "Offending command was: \"%s\", name: %s, password: %s" : "Onjuiste commando was: \"%s\", naam: %s, wachtwoord: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Instellen van de gebruik rechten voor %s is mislukt, omdat de rechten hoger zijn dan de aan %s toegekende gebruik rechten", "Setting permissions for %s failed, because the item was not found" : "Instellen van de gebruik rechten voor %s is mislukt, omdat het object niet is gevonden", "Cannot clear expiration date. Shares are required to have an expiration date." : "Kan verval datum niet weghalen. Gedeelte folders moeten een vervaldatum hebben.", "Cannot increase permissions of %s" : "Kan de rechten van %s niet verhogen.", "Files can't be shared with delete permissions" : "Bestanden kunnen niet worden gedeeld met verwijder rechten", "Files can't be shared with create permissions" : "Bestanden kunnen niet worden gedeeld met creëer rechten", "Cannot set expiration date more than %s days in the future" : "Kan de vervaldatum niet meer dan %s dagen in de toekomst instellen", "Personal" : "Persoonlijk", "Admin" : "Beheerder", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Dit kan hersteld worden door de webserver schrijfrechten te %s geven op de appsdirectory %s of door de appstore te deactiveren in het configuratie bestand.", "Cannot create \"data\" directory (%s)" : "Kan de \"data\" directory (%s) niet aanmaken", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Dit kan hersteld worden door <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> de webserver schrijfrechten te geven tot de hoofd directory</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Toegang kan hersteld worden door %s in de hoofd directory %s op de webserver schrijfrechten te geven.", "Data directory (%s) is readable by other users" : "De data directory (%s) is alleen lezen voor andere gebruikers", "Data directory (%s) must be an absolute path" : "De data directory (%s) moet een absolute bestand locatie hebben", "Data directory (%s) is invalid" : "Data directory (%s) is ongeldig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Verifieer dat de data directory een bestand \".ocdata\" in de hoofdmap heeft." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/sv.json 0000604 00000054217 15247130447 0006655 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Kan inte skriva till \"config\" katalogen!", "This can usually be fixed by giving the webserver write access to the config directory" : "Detta kan vanligtvis åtgärdas genom att ge skrivrättigheter till config-katalogen", "See %s" : "Se %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Detta fixas vanligtvis genom att ge webbservern skrivrättigheter till konfigureringsmappen. Se %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Filerna i appen %$1s ersattes inte korrekt. Se till att det är en version som är kompatibel med servern.", "Sample configuration detected" : "Exempel-konfiguration detekterad", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Det har detekterats att exempel-konfigurationen har kopierats. Detta kan förstöra din installation och stöds ej. Vänligen läs dokumentationen innan ändringar på config.php utförs", "%1$s and %2$s" : "%1$s och %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s och %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s och %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s och %5$s", "Education Edition" : "Utbildningsversion", "Enterprise bundle" : "Företagspaket", "Groupware bundle" : "Groupware-paket", "Social sharing bundle" : "Social delnings-paket", "PHP %s or higher is required." : "PHP %s eller högre krävs.", "PHP with a version lower than %s is required." : "PHP med version lägre än %s krävs.", "%sbit or higher PHP required." : "%sbit eller nyare PHP-version krävs.", "Following databases are supported: %s" : "Följande databastyper stöds: %s", "The command line tool %s could not be found" : "Kommandoradsverktyget %s hittades inte.", "The library %s is not available." : "Biblioteket %s är inte tillgängligt.", "Library %s with a version higher than %s is required - available version %s." : "Bibliotek %s med version högre än %s krävs - tillgänglig version %s.", "Library %s with a version lower than %s is required - available version %s." : "Bibliotek %s med version lägre än %s krävs - tillgänglig version %s.", "Following platforms are supported: %s" : "Följande plattformar stöds: %s", "Server version %s or higher is required." : "Serverversion %s eller nyare krävs.", "Server version %s or lower is required." : "Serverversion %s eller äldre krävs.", "Unknown filetype" : "Okänd filtyp", "Invalid image" : "Ogiltig bild", "Avatar image is not square" : "Profilbilden är inte fyrkantig", "today" : "i dag", "yesterday" : "i går", "_%n day ago_::_%n days ago_" : ["%n dag sedan","%n dagar sedan"], "last month" : "förra månaden", "_%n month ago_::_%n months ago_" : ["%n månad sedan","%n månader sedan"], "last year" : "förra året", "_%n year ago_::_%n years ago_" : ["%n år sedan","%n år sedan"], "_%n hour ago_::_%n hours ago_" : ["%n timme sedan","%n timmar sedan"], "_%n minute ago_::_%n minutes ago_" : ["%n minut sedan","%n minuter sedan"], "seconds ago" : "sekunder sedan", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finns inte längre. Vänligen aktivera det i dina appinställningar eller kontakta din administratör.", "File name is a reserved word" : "Filnamnet är ett reserverat ord", "File name contains at least one invalid character" : "Filnamnet innehåller minst ett ogiltigt tecken", "File name is too long" : "Filnamnet är för långt", "Dot files are not allowed" : "Dot-filer är inte tillåtna", "Empty filename is not allowed" : "Tomma filnamn är inte tillåtna", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Applikationen \"%s\" kan ej installeras eftersom informationen från appen ej kunde läsas.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Applikationen \"%s\" kan ej installeras eftersom den inte är kompatibel med denna serverversion.", "This is an automatically sent email, please do not reply." : "Detta är ett automatiskt skickat e-postmeddelande, svara inte på detta mejl.", "Help" : "Hjälp", "Apps" : "Applikationer", "Settings" : "Inställningar", "Log out" : "Logga ut", "Users" : "Användare", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Vanliga inställningar", "Sharing" : "Delning", "Security" : "Säkerhet", "Encryption" : "Kryptering", "Additional settings" : "Övriga inställningar", "Tips & tricks" : "Tips & tricks", "Personal info" : "Personlig information", "Sync clients" : "Synkklienter", "Unlimited" : "Obegränsad", "__language_name__" : "__language_name__", "Verifying" : "Verifierar", "Verifying …" : "Verifierar ...", "Verify" : "Verifiera", "%s enter the database username and name." : "%s ange användarnamn och namn för databasen.", "%s enter the database username." : "%s ange databasanvändare.", "%s enter the database name." : "%s ange databasnamn", "%s you may not use dots in the database name" : "%s du får inte använda punkter i databasnamnet", "Oracle connection could not be established" : "Oracle-anslutning kunde inte etableras", "Oracle username and/or password not valid" : "Oracle-användarnamnet och/eller lösenordet är felaktigt", "PostgreSQL username and/or password not valid" : "PostgreSQL-användarnamnet och/eller lösenordet är felaktigt", "You need to enter details of an existing account." : "Du måste ange inloggningsuppgifter av ett aktuellt konto.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X stöds inte och %s kommer inte att fungera korrekt på denna plattform. Använd på egen risk!", "For the best results, please consider using a GNU/Linux server instead." : "För bästa resultat, överväg att använda en GNU/Linux server istället.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Det verkar som om denna %s instans körs på en 32-bitars PHP miljö och open_basedir har konfigurerats i php.ini. Detta kommer att leda till problem med filer över 4 GB och är verkligen inte rekommenderat!", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Ta bort open_basedir i din php.ini eller byt till 64-bitars PHP.", "Set an admin username." : "Ange ett användarnamn för administratören.", "Set an admin password." : "Ange ett administratörslösenord.", "Can't create or write into the data directory %s" : "Kan inte skapa eller skriva till data-katalogen %s", "Invalid Federated Cloud ID" : "Ogiltigt Federerat Moln-ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Misslyckades dela ut %s då backend inte tillåter delningar från typ %i", "Sharing %s failed, because the file does not exist" : "Delning av %s misslyckades på grund av att filen inte existerar", "You are not allowed to share %s" : "Du har inte rätt att dela %s", "Sharing %s failed, because you can not share with yourself" : "Delning %s misslyckades därför att du inte kan dela med dig själv.", "Sharing %s failed, because the user %s does not exist" : "Delning %s misslyckades därför att användaren %s inte existerar", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Delning %s misslyckades därför att användaren %s inte är medlem i någon utav de grupper som %s är medlem i", "Sharing %s failed, because this item is already shared with %s" : "Delning %s misslyckades därför att objektet redan är delat med %s", "Sharing %s failed, because this item is already shared with user %s" : "Delning %s misslyckades därför att detta redan är delat med användaren %s", "Sharing %s failed, because the group %s does not exist" : "Delning %s misslyckades därför att gruppen %s inte existerar", "Sharing %s failed, because %s is not a member of the group %s" : "Delning %s misslyckades därför att %s inte ingår i gruppen %s", "You need to provide a password to create a public link, only protected links are allowed" : "Du måste ange ett lösenord för att skapa en offentlig länk, endast skyddade länkar är tillåtna", "Sharing %s failed, because sharing with links is not allowed" : "Delning %s misslyckades därför att delning utav länkar inte är tillåtet", "Not allowed to create a federated share with the same user" : "Ej tillåtet att skapa en federerad delning med samma användare", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Misslyckades dela ut %s, kan inte hitta %s, kanske är servern inte åtkomlig för närvarande.", "Share type %s is not valid for %s" : "Delningstyp %s är inte giltig för %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Kan inte sätta utgångsdatum. Utdelningar kan inte utgå senare än %s efter de har delats ut", "Cannot set expiration date. Expiration date is in the past" : "Kan inte sätta utgångsdatum. Utgångsdatumet är i det förflutna.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Delningsgränssnittet %s måste implementera gränssnittet OCP\\Share_Backend", "Sharing backend %s not found" : "Delningsgränssnittet %s hittades inte", "Sharing backend for %s not found" : "Delningsgränssnittet för %s hittades inte", "Sharing failed, because the user %s is the original sharer" : "Delning misslyckades eftersom användaren %s redan är den som har delat detta.", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Delning %s misslyckades därför att rättigheterna överskrider de rättigheter som är tillåtna för %s", "Sharing %s failed, because resharing is not allowed" : "Delning %s misslyckades därför att vidaredelning inte är tillåten", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Delning %s misslyckades därför att delningsgränsnittet för %s inte kunde hitta sin källa", "Sharing %s failed, because the file could not be found in the file cache" : "Delning %s misslyckades därför att filen inte kunde hittas i filcachen", "Can’t increase permissions of %s" : "Kan inte öka rättigheterna för %s", "Files can’t be shared with delete permissions" : "Filer kan inte delas med borttagningsrättigheter", "Files can’t be shared with create permissions" : "Filer kan inte delas med rättigheter att skapa", "Expiration date is in the past" : "Utgångsdatum är i det förflutna", "Can’t set expiration date more than %s days in the future" : "Kan inte sätta utgångsdatum mer än %s dagar framåt", "%s shared »%s« with you" : "%s delade »%s« med dig", "%s shared »%s« with you." : "%s delade »%s« med dig.", "Click the button below to open it." : "Klicka knappen nedan för att öppna det.", "Open »%s«" : "Öppna »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Den begärda delningen finns inte mer", "Could not find category \"%s\"" : "Kunde inte hitta kategorin \"%s\"", "Sunday" : "Söndag", "Monday" : "Måndag", "Tuesday" : "Tisdag", "Wednesday" : "Onsdag", "Thursday" : "Torsdag", "Friday" : "Fredag", "Saturday" : "Lördag", "Sun." : "Sön.", "Mon." : "Mån.", "Tue." : "Tis.", "Wed." : "Ons.", "Thu." : "Tors.", "Fri." : "Fre.", "Sat." : "Lör.", "Su" : "Sö", "Mo" : "Må", "Tu" : "Ti", "We" : "On", "Th" : "To", "Fr" : "Fr", "Sa" : "Lö", "January" : "Januari", "February" : "Februari", "March" : "Mars", "April" : "April", "May" : "Maj", "June" : "Juni", "July" : "Juli", "August" : "Augusti", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Maj.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Endast följande tecken är tillåtna i användarnamnet: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Ett giltigt användarnamn måste anges", "Username contains whitespace at the beginning or at the end" : "Användarnamnet består av ett mellanslag i början eller i slutet", "Username must not consist of dots only" : "Användarnamnet får inte innehålla enbart punkter", "A valid password must be provided" : "Ett giltigt lösenord måste anges", "The username is already being used" : "Användarnamnet används redan", "Could not create user" : "Kunde inte skapa användare", "User disabled" : "Användare inaktiverad", "Login canceled by app" : "Inloggningen avbruten av appen", "No app name specified" : "Inget appnamn angivet", "App '%s' could not be installed!" : "Applikationen \"%s\" gick inte att installera!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Applikationen \"%s\" kan ej installeras eftersom följande kriterier inte är uppfyllda: %s", "a safe home for all your data" : "En säker plats för dina filer och data", "File is currently busy, please try again later" : "Filen är för tillfället upptagen, vänligen försök igen senare", "Can't read file" : "Kan ej läsa filen", "Application is not enabled" : "Applikationen är inte aktiverad", "Authentication error" : "Fel vid autentisering", "Token expired. Please reload page." : "Ogiltig token. Ladda om sidan.", "Unknown user" : "Okänd användare", "No database drivers (sqlite, mysql, or postgresql) installed." : "Inga databasdrivrutiner (sqlite, mysql, eller postgresql) installerade.", "Cannot write into \"config\" directory" : "Kan inte skriva till \"config\" katalogen", "Cannot write into \"apps\" directory" : "Kan inte skriva till \"apps\" katalogen!", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Detta kan vanligtvis fixas genom att ge webbservern skrivåtkomst till mappen för appar eller genom att avaktivera App store i konfigurationsfilen. Se %s", "Cannot create \"data\" directory" : "Kan inte skapa \"datamapp\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Detta kan vanligtvis fixas genom att ge webbservern skrivåtkomst till rotkatalogen. Se %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Rättigheter kan vanligtvis fixas genom att ge webbservern skrivåtkomst till rotkatalogen. Se %s.", "Setting locale to %s failed" : "Sätta locale till %s misslyckades", "Please install one of these locales on your system and restart your webserver." : "Vänligen installera en av dessa locale på din server och starta om din webbserver,", "Please ask your server administrator to install the module." : "Vänligen be din administratör att installera modulen.", "PHP module %s not installed." : "PHP-modulen %s är inte installerad.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-inställning \"%s\" är inte inställd på \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Att ändra denna inställning i php.ini kommer göra så att Nextcloud fungerar igen", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload är satt till \"%s\" istället för det förväntade värdet \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "För att åtgärda detta problem sätt värdet <code> mbstring.func_overload till </ code> <code> 0 </ code> i din php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 är det minsta som krävs. För närvarande är %s installerat.", "To fix this issue update your libxml2 version and restart your web server." : "För att åtgärda detta problem uppdatera libxml2 versionen och starta om din webbserver.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP är tydligen inställt för att tömma \"inline doc blocks\". Detta kommer att göra flera kärnprogram otillgängliga.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Detta orsakas troligtvis av en cache/accelerator som t ex Zend OPchache eller eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-moduler har installerats, men de listas fortfarande som saknade?", "Please ask your server administrator to restart the web server." : "Vänligen be din serveradministratör att starta om webbservern.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 krävs", "Please upgrade your database version" : "Vänligen uppgradera din databas-version", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Vänligen ändra rättigheterna till 0770 så att katalogen inte kan listas utav andra användare.", "Your data directory is readable by other users" : "Din datamapp är läsbar av andra användare", "Your data directory must be an absolute path" : "Du måste specificera en korrekt sökväg till datamappen", "Check the value of \"datadirectory\" in your configuration" : "Kontrollera värdet av \"datakatalog\" i din konfiguration", "Your data directory is invalid" : "Din datamapp är ogiltig", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Säkerställ att du har filen \".ocdata\" i huvudkatalogen för din data.", "Could not obtain lock type %d on \"%s\"." : "Kunde inte hämta låstyp %d på \"%s\".", "Storage unauthorized. %s" : "Lagringsutrymme ej tillåtet. %s", "Storage incomplete configuration. %s" : "Lagringsutrymme felaktigt inställt. %s", "Storage connection error. %s" : "Lagringsutrymme lyckas inte ansluta. %s", "Storage is temporarily not available" : "Lagringsutrymme är för tillfället inte tillgängligt", "Storage connection timeout. %s" : "Lagringsutrymme lyckas inte ansluta \"timeout\". %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Detta kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till konfigurations-katalogen %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulen med id: %s finns inte. Vänligen aktivera det i dina app-inställningar eller kontakta din administratör.", "Server settings" : "Serverinställningar", "DB Error: \"%s\"" : "DB-fel: \"%s\"", "Offending command was: \"%s\"" : "Det felaktiga kommandot var: \"%s\"", "You need to enter either an existing account or the administrator." : "Du måste antingen ange ett befintligt konto eller administratör.", "Offending command was: \"%s\", name: %s, password: %s" : "Det felande kommandot var: \"%s\", namn: %s, lösenord: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Misslyckades att sätta rättigheter för %s därför att rättigheterna överskrider de som är tillåtna för %s", "Setting permissions for %s failed, because the item was not found" : "Att sätta rättigheterna för %s misslyckades därför att objektet inte hittades", "Cannot clear expiration date. Shares are required to have an expiration date." : "Kan ej ta bort utgångsdatumet. Delningen kräver att det finns ett utgångsdatum.", "Cannot increase permissions of %s" : "Kan ej öka behörigheterna för %s", "Files can't be shared with delete permissions" : "Filerna kan ej delas med \"radera behörigheter\"", "Files can't be shared with create permissions" : "Filerna kan ej delas med \"skapa behörigheter\"", "Cannot set expiration date more than %s days in the future" : "Kan ej välja ett utgångsdatum längre fram än %s dagar", "Personal" : "Personliga Inställningar", "Admin" : "Administration", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Detta kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till applikationskatalogen %s eller stänga av app-butik i konfigurationsfilen.", "Cannot create \"data\" directory (%s)" : "Kan inte skapa \"data\" katalog (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Detta kan vanligtvis åtgärdas genom att <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> ge webbserver skrivåtkomst till rotkatalogen </a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Rättigheterna kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till rotkatalogen %s.", "Data directory (%s) is readable by other users" : "Datakatalogen (%s) kan läsas av andra användare", "Data directory (%s) must be an absolute path" : "Datakatalogen (%s) måste vara hela sökvägen", "Data directory (%s) is invalid" : "Datamappen (%s) är ogiltig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Vänligen kontrollera att datakatalogen innehåller filen \".ocdata\" i rooten." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/ko.json 0000604 00000051617 15247130447 0006637 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "\"config\" 디렉터리에 기록할 수 없습니다!", "This can usually be fixed by giving the webserver write access to the config directory" : "config 디렉터리에 웹 서버 쓰기 권한을 주면 해결됩니다", "See %s" : "%s 보기", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "앱 %1$s의 파일이 올바르게 교체되지 않았습니다. 서버와 호환되는 버전인지 확인하십시오.", "Sample configuration detected" : "예제 설정 감지됨", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "예제 설정이 복사된 것 같습니다. 올바르게 작동하지 않을 수도 있기 때문에 지원되지 않습니다. config.php를 변경하기 전 문서를 읽어 보십시오", "%1$s and %2$s" : "%1$s 및 %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s 및 %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s 및 %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s 및 %5$s", "Enterprise bundle" : "엔터프라이즈 번들", "Groupware bundle" : "그룹웨어 번들", "Social sharing bundle" : "소셜 공유 번들", "PHP %s or higher is required." : "PHP 버전 %s 이상이 필요합니다.", "PHP with a version lower than %s is required." : "PHP 버전 %s 미만이 필요합니다.", "%sbit or higher PHP required." : "%s비트 이상의 PHP가 필요합니다.", "Following databases are supported: %s" : "다음 데이터베이스를 지원합니다: %s", "The command line tool %s could not be found" : "명령행 도구 %s을(를) 찾을 수 없습니다", "The library %s is not available." : "%s 라이브러리를 사용할 수 없습니다.", "Library %s with a version higher than %s is required - available version %s." : "%s 라이브러리의 버전 %s 이상이 필요합니다. 사용 가능한 버전은 %s입니다.", "Library %s with a version lower than %s is required - available version %s." : "%s 라이브러리의 버전 %s 미만이 필요합니다. 사용 가능한 버전은 %s입니다.", "Following platforms are supported: %s" : "다음 플랫폼을 지원합니다: %s", "Server version %s or higher is required." : "서버 버전 %s 이상이 필요합니다.", "Server version %s or lower is required." : "서버 버전 %s 미만이 필요합니다.", "Unknown filetype" : "알 수 없는 파일 형식", "Invalid image" : "잘못된 사진", "Avatar image is not square" : "아바타 사진이 정사각형이 아님", "today" : "오늘", "yesterday" : "어제", "_%n day ago_::_%n days ago_" : ["%n일 전"], "last month" : "지난 달", "_%n month ago_::_%n months ago_" : ["%n달 전 "], "last year" : "작년", "_%n year ago_::_%n years ago_" : ["%n년 전"], "_%n hour ago_::_%n hours ago_" : ["%n시간 전"], "_%n minute ago_::_%n minutes ago_" : ["%n분 전"], "seconds ago" : "초 전", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID: %s인 모듈이 존재하지 않습니다. 앱 설정에서 확인하거나 시스템 관리자에게 연락하십시오.", "File name is a reserved word" : "파일 이름이 예약된 단어임", "File name contains at least one invalid character" : "파일 이름에 잘못된 글자가 한 자 이상 있음", "File name is too long" : "파일 이름이 너무 김", "Dot files are not allowed" : "점으로 시작하는 파일은 허용되지 않음", "Empty filename is not allowed" : "파일 이름을 비워 둘 수 없음", "App \"%s\" cannot be installed because appinfo file cannot be read." : "appinfo 파일을 읽을 수 없어서 앱 \"%s\"을(를) 설치할 수 없습니다.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "이 서버 버전과 호환되지 않아서 앱 \"%s\"을(를) 설치할 수 없습니다", "This is an automatically sent email, please do not reply." : "자동으로 전송한 이메일입니다. 답장하지 마십시오.", "Help" : "도움말", "Apps" : "앱", "Log out" : "로그아웃", "Users" : "사용자", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "기본 설정", "Sharing" : "공유", "Security" : "보안", "Encryption" : "암호화", "Additional settings" : "고급 설정", "Tips & tricks" : "팁과 추가 정보", "%s enter the database username and name." : "%s 데이터베이스 사용자 이름과 이름을 입력해 주십시오.", "%s enter the database username." : "%s 데이터베이스 사용자 이름을 입력해 주십시오.", "%s enter the database name." : "%s 데이터베이스 이름을 입력하십시오.", "%s you may not use dots in the database name" : "%s 데이터베이스 이름에는 마침표를 사용할 수 없습니다", "Oracle connection could not be established" : "Oracle 연결을 수립할 수 없습니다.", "Oracle username and/or password not valid" : "Oracle 사용자 이름이나 암호가 잘못되었습니다.", "PostgreSQL username and/or password not valid" : "PostgreSQL 사용자 이름 또는 암호가 잘못되었습니다", "You need to enter details of an existing account." : "존재하는 계정 정보를 입력해야 합니다.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X은 지원하지 않으며 %s이(가) 이 플랫폼에서 올바르게 작동하지 않을 수도 있습니다. 본인 책임으로 사용하십시오! ", "For the best results, please consider using a GNU/Linux server instead." : "더 좋은 결과를 얻으려면 GNU/Linux 서버를 사용하는 것을 권장합니다.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "%s 인스턴스가 32비트 PHP 환경에서 실행 중이고 php.ini에 open_basedir이 설정되어 있습니다. 4GB 이상의 파일 처리에 문제가 생길 수 있으므로 추천하지 않습니다.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "php.ini의 open_basedir 설정을 삭제하거나 64비트 PHP로 전환하십시오.", "Set an admin username." : "관리자의 사용자 이름을 설정합니다.", "Set an admin password." : "관리자의 암호를 설정합니다.", "Can't create or write into the data directory %s" : "데이터 디렉터리 %s을(를) 만들거나 기록할 수 없음", "Invalid Federated Cloud ID" : "잘못된 연합 클라우드 ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "%s을(를) 공유할 수 없습니다. 백엔드에서 %i 형식의 공유를 허용하지 않습니다", "Sharing %s failed, because the file does not exist" : "%s을(를) 공유할 수 없습니다. 파일이 존재하지 않습니다", "You are not allowed to share %s" : "%s을(를) 공유할 수 있는 권한이 없습니다", "Sharing %s failed, because you can not share with yourself" : "%s을(를) 공유할 수 없습니다. 자기 자신과 공유할 수 없습니다", "Sharing %s failed, because the user %s does not exist" : "%s을(를) 공유할 수 없습니다. 사용자 %s이(가) 존재하지 않습니다", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s을(를) 공유할 수 없습니다. 사용자 %s 님은 %s 님이 회원인 어떠한 그룹에도 속해 있지 않습니다", "Sharing %s failed, because this item is already shared with %s" : "%s을(를) 공유할 수 없습니다. 이미 %s 님과 공유되어 있습니다", "Sharing %s failed, because this item is already shared with user %s" : "%s을(를) 공유할 수 없습니다. 이 항목을 이미 %s 님과 공유하고 있습니다", "Sharing %s failed, because the group %s does not exist" : "%s을(를) 공유할 수 없습니다. 그룹 %s이(가) 존재하지 않습니다", "Sharing %s failed, because %s is not a member of the group %s" : "%s을(를) 공유할 수 없습니다. %s 님이 그룹 %s의 구성원이 아닙니다", "You need to provide a password to create a public link, only protected links are allowed" : "공개 링크를 만들려면 암호를 입력해야 합니다. 보호된 링크만 사용 가능합니다", "Sharing %s failed, because sharing with links is not allowed" : "%s을(를) 공유할 수 없습니다. 링크 공유가 허용되지 않았습니다", "Not allowed to create a federated share with the same user" : "같은 사용자와 연합 공유를 만들 수 없음", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s을(를) 공유할 수 없습니다. %s을(를) 찾을 수 없습니다. 서버에 접근하지 못할 수도 있습니다.", "Share type %s is not valid for %s" : "공유 형식 %s을(를) %s에 대해서 사용할 수 없음", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "만료 날짜를 설정할 수 없습니다. 최대 공유 허용 기한이 %s입니다.", "Cannot set expiration date. Expiration date is in the past" : "만료 날짜를 설정할 수 없습니다. 만료 날짜가 과거입니다", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "공유 백엔드 %s에서 OCP\\Share_Backend 인터페이스를 구현해야 함", "Sharing backend %s not found" : "공유 백엔드 %s을(를) 찾을 수 없음", "Sharing backend for %s not found" : "%s의 공유 백엔드를 찾을 수 없음", "Sharing failed, because the user %s is the original sharer" : "공유할 수 없습니다. 사용자 %s이(가) 원 공유자입니다", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "%s을(를) 공유할 수 없습니다. %s 님에게 허용된 것 이상의 권한을 필요로 합니다", "Sharing %s failed, because resharing is not allowed" : "%s을(를) 공유할 수 없습니다. 다시 공유할 수 없습니다", "Sharing %s failed, because the sharing backend for %s could not find its source" : "%s을(를) 공유할 수 없습니다. %s의 공유 백엔드에서 원본 파일을 찾을 수 없습니다", "Sharing %s failed, because the file could not be found in the file cache" : "%s을(를) 공유할 수 없습니다. 파일 캐시에서 찾을 수 없습니다", "Expiration date is in the past" : "만료 날짜가 과거입니다", "%s shared »%s« with you" : "%s 님이 %s을(를) 공유했습니다", "%s via %s" : "%s(%s 경유)", "Could not find category \"%s\"" : "분류 \"%s\"을(를) 찾을 수 없습니다", "Sunday" : "일요일", "Monday" : "월요일", "Tuesday" : "화요일", "Wednesday" : "수요일", "Thursday" : "목요일", "Friday" : "금요일", "Saturday" : "토요일", "Sun." : "일", "Mon." : "월", "Tue." : "화", "Wed." : "수", "Thu." : "목", "Fri." : "금", "Sat." : "토", "Su" : "일", "Mo" : "월", "Tu" : "화", "We" : "수", "Th" : "목", "Fr" : "금", "Sa" : "토", "January" : "1월", "February" : "2월", "March" : "3월", "April" : "4월", "May" : "5월", "June" : "6월", "July" : "7월", "August" : "8월", "September" : "9월", "October" : "10월", "November" : "11월", "December" : "12월", "Jan." : "1월", "Feb." : "2월", "Mar." : "3월", "Apr." : "4월", "May." : "5월", "Jun." : "6월", "Jul." : "7월", "Aug." : "8월", "Sep." : "9월", "Oct." : "10월", "Nov." : "11월", "Dec." : "12월", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "다음 문자만 이름에 사용할 수 있습니다: \"a-z\", \"A-Z\", \"0-9\", 및 \"_.@-'\"", "A valid username must be provided" : "올바른 사용자 이름을 입력해야 합니다", "Username contains whitespace at the beginning or at the end" : "사용자 이름의 시작이나 끝에 공백이 있습니다", "Username must not consist of dots only" : "사용자 이름에 마침표만 있으면 안 됩니다", "A valid password must be provided" : "올바른 암호를 입력해야 합니다", "The username is already being used" : "사용자 이름이 이미 존재합니다", "User disabled" : "사용자 비활성화", "Login canceled by app" : "앱 로그인 취소", "No app name specified" : "앱 이름이 지정되지 않았음", "App '%s' could not be installed!" : "앱 '%s'을(를) 설치할 수 없습니다!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "앱 \"%s\"의 다음 의존성을 만족하지 못하므로 설치할 수 없습니다: %s", "a safe home for all your data" : "내 모든 데이터의 안전한 저장소", "File is currently busy, please try again later" : "파일이 현재 사용 중, 나중에 다시 시도하십시오", "Can't read file" : "파일을 읽을 수 없음", "Application is not enabled" : "앱이 활성화되지 않았습니다", "Authentication error" : "인증 오류", "Token expired. Please reload page." : "토큰이 만료되었습니다. 페이지를 새로 고치십시오.", "Unknown user" : "알려지지 않은 사용자", "No database drivers (sqlite, mysql, or postgresql) installed." : "데이터베이스 드라이버(sqlite, mysql, postgresql)가 설치되지 않았습니다.", "Cannot write into \"config\" directory" : "\"config\" 디렉터리에 기록할 수 없습니다", "Cannot write into \"apps\" directory" : "\"apps\" 디렉터리에 기록할 수 없습니다", "Cannot create \"data\" directory" : "\"data\" 디렉터리를 만들 수 없음", "Setting locale to %s failed" : "로캘을 %s(으)로 설정할 수 없음", "Please install one of these locales on your system and restart your webserver." : "다음 중 하나 이상의 로캘을 시스템에 설치하고 웹 서버를 다시 시작하십시오.", "Please ask your server administrator to install the module." : "서버 관리자에게 모듈 설치를 요청하십시오.", "PHP module %s not installed." : "PHP 모듈 %s이(가) 설치되지 않았습니다.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP 설정 \"%s\"이(가) \"%s\"(으)로 설정되어 있지 않습니다.", "Adjusting this setting in php.ini will make Nextcloud run again" : "php.ini 파일에서 설정을 변경하면 Nextcloud가 다시 실행됩니다", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload 값이 \"%s\"(으)로 설정되어 있으나 \"0\"으로 설정해야 함", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "이 문제를 해결하려면 php.ini에서 <code>mbstring.func_overload</code> 값을 <code>0</code>으로 설정하십시오", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 이상이 필요합니다. 현재 버전은 %s입니다.", "To fix this issue update your libxml2 version and restart your web server." : "이 문제를 해결하려면 libxml2 버전을 업데이트하고 웹 서버를 다시 시작하십시오.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP에서 인라인 문서 블록을 삭제하도록 설정되어 있습니다. 일부 코어 앱을 사용하지 못할 수도 있습니다.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Zend OPcache, eAccelerator 같은 캐시/가속기 문제일 수도 있습니다.", "PHP modules have been installed, but they are still listed as missing?" : "PHP 모듈이 설치되었지만 여전히 없는 것으로 나타납니까?", "Please ask your server administrator to restart the web server." : "서버 관리자에게 웹 서버 재시작을 요청하십시오.", "PostgreSQL >= 9 required" : "PostgreSQL 버전 9 이상이 필요합니다", "Please upgrade your database version" : "데이터베이스 버전을 업그레이드 하십시오", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "권한을 0770으로 변경하여 다른 사용자가 읽을 수 없도록 하십시오.", "Your data directory is readable by other users" : "내 데이터 디렉터리를 다른 사용자가 읽을 수 있음", "Your data directory must be an absolute path" : "내 데이터 디렉터리는 절대 경로여야 함", "Check the value of \"datadirectory\" in your configuration" : "설정 중 \"datadirectory\" 값을 확인하십시오", "Your data directory is invalid" : "내 데이터 디렉터리가 잘못됨", "Could not obtain lock type %d on \"%s\"." : "잠금 형식 %d을(를) \"%s\"에 대해 얻을 수 없습니다.", "Storage unauthorized. %s" : "저장소가 인증되지 않았습니다. %s", "Storage incomplete configuration. %s" : "저장소 설정이 완전하지 않습니다. %s", "Storage connection error. %s" : "저장소 연결 오류. %s", "Storage is temporarily not available" : "저장소를 임시로 사용할 수 없음", "Storage connection timeout. %s" : "저장소 연결 시간 초과. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "%sconfig 디렉터리에 웹 서버 쓰기 권한%s을 주면 해결됩니다.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID: %s인 모듈이 존재하지 않습니다. 앱 설정에서 활성화하거나 관리자에게 연락하십시오.", "Server settings" : "서버 설정", "DB Error: \"%s\"" : "DB 오류: \"%s\"", "Offending command was: \"%s\"" : "잘못된 명령: \"%s\"", "You need to enter either an existing account or the administrator." : "기존 계정이나 administrator(관리자)를 입력해야 합니다.", "Offending command was: \"%s\", name: %s, password: %s" : "잘못된 명령: \"%s\", 이름: %s, 암호: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "%s의 권한을 설정할 수 없습니다. %s 님에게 허용된 것 이상의 권한을 필요로 합니다", "Setting permissions for %s failed, because the item was not found" : "%s의 권한을 설정할 수 없습니다. 항목을 찾을 수 없습니다", "Cannot clear expiration date. Shares are required to have an expiration date." : "만료 날짜를 비워 둘 수 없습니다. 공유되는 항목에는 만료 날짜가 필요합니다.", "Cannot increase permissions of %s" : "%s의 권한을 늘릴 수 없습니다", "Files can't be shared with delete permissions" : "파일을 삭제 권한으로 공유할 수 없습니다", "Files can't be shared with create permissions" : "파일을 생성 권한으로 공유할 수 없습니다", "Cannot set expiration date more than %s days in the future" : "만료 날짜를 %s일 이상 이후로 설정할 수 없습니다", "Personal" : "개인", "Admin" : "관리자", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "%sapps 디렉터리에 웹 서버 쓰기 권한%s을 주거나 설정 파일에서 앱 스토어를 비활성화하면 해결됩니다.", "Cannot create \"data\" directory (%s)" : "\"data\" 디렉터리를 만들 수 없음(%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">루트 디렉터리에 웹 서버 쓰기 권한</a>을 주면 해결됩니다.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "%s루트 디렉터리에 웹 서버 쓰기 권한%s을 주면 해결됩니다.", "Data directory (%s) is readable by other users" : "데이터 디렉터리(%s)를 다른 사용자가 읽을 수 있음", "Data directory (%s) must be an absolute path" : "데이터 디렉터리(%s)는 반드시 절대 경로여야 함", "Data directory (%s) is invalid" : "데이터 디렉터리(%s)가 잘못됨", "Please check that the data directory contains a file \".ocdata\" in its root." : "데이터 디렉터리의 최상위 경로에 \".ocdata\" 파일이 있는지 확인하십시오." },"pluralForm" :"nplurals=1; plural=0;" } l10n/zh_CN.json 0000604 00000047574 15247130447 0007236 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "无法写入 \"config\" 目录!ond", "This can usually be fixed by giving the webserver write access to the config directory" : "您可以设置 Web 服务器对 config 目录的写权限修复这个问题", "See %s" : "查看 %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "这个通常可以通过赋予写入权限到 config 目录来修复。查看:%s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "应用 %$1s 的文件替换不正确. 请确认版本与当前服务器兼容.", "Sample configuration detected" : "示例配置检测", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "您似乎直接把 config.php 的样例文件直接复制使用. 这可能会破坏您的安装. 在对 config.php 进行修改之前请先阅读相关文档.", "%1$s and %2$s" : "%1$s 和 %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s 和 %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s 和 %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s 和 %5$s", "Education Edition" : "教育版", "Enterprise bundle" : "企业捆绑包", "Groupware bundle" : "群组捆绑包", "Social sharing bundle" : "社交分享捆绑包", "PHP %s or higher is required." : "要求 PHP 版本 %s 或者更高。", "PHP with a version lower than %s is required." : "需要版本低于 %s 的PHP.", "%sbit or higher PHP required." : "需要 %s 或更高版本的 PHP", "Following databases are supported: %s" : "支持以下数据库: %s", "The command line tool %s could not be found" : "命令行工具 %s 未找到", "The library %s is not available." : "库文件 %s 不可用", "Library %s with a version higher than %s is required - available version %s." : "%s 需要 %s 或更高的版本 - 可用版本 %s.", "Library %s with a version lower than %s is required - available version %s." : "%s 需要 %s 或更低的版本 - 可用版本 %s.", "Following platforms are supported: %s" : "支持以下平台:%s", "Server version %s or higher is required." : "需要服务器版本 %s 或更高版本。", "Server version %s or lower is required." : "需要服务器版本 %s 或更低版本。", "Unknown filetype" : "未知的文件类型", "Invalid image" : "无效的图像", "Avatar image is not square" : "头像图像不是正方形", "today" : "今天", "yesterday" : "昨天", "_%n day ago_::_%n days ago_" : ["%n 天前"], "last month" : "上月", "_%n month ago_::_%n months ago_" : ["%n 月前"], "last year" : "去年", "_%n year ago_::_%n years ago_" : ["%n 年前"], "_%n hour ago_::_%n hours ago_" : ["%n 小时前"], "_%n minute ago_::_%n minutes ago_" : ["%n 分钟前"], "seconds ago" : "几秒前", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "模块:%s不存在。请在 App 设置中开启或联系管理员。", "File name is a reserved word" : "文件名包含敏感字符", "File name contains at least one invalid character" : "文件名中存在至少一个非法字符", "File name is too long" : "文件名过长", "Dot files are not allowed" : ".文件 不被允许", "Empty filename is not allowed" : "不允许使用空名称。", "App \"%s\" cannot be installed because appinfo file cannot be read." : "无法安装应用\"%s\",因为无法读取appinfo文件.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "应用程式 \"%s\" 无法安装,因为它与这个版本的服务器不兼容.", "This is an automatically sent email, please do not reply." : "这是一个自动生成的电子邮件,请不要回复。", "Help" : "帮助", "Apps" : "应用", "Settings" : "设置", "Log out" : "注销", "Users" : "用户", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "基本设置", "Sharing" : "分享", "Security" : "安全", "Encryption" : "加密", "Additional settings" : "其他设置", "Tips & tricks" : "小提示", "Personal info" : "个人信息", "Sync clients" : "同步客户", "Unlimited" : "无限制", "__language_name__" : "简体中文", "Verifying" : "验证", "Verifying …" : "验证...", "Verify" : "验证", "%s enter the database username and name." : "%s 输入数据库用户名和名称.", "%s enter the database username." : "%s 输入数据库用户名。", "%s enter the database name." : "%s 输入数据库名称。", "%s you may not use dots in the database name" : "%s 您不能在数据库名称中使用英文句号。", "Oracle connection could not be established" : "不能建立甲骨文连接", "Oracle username and/or password not valid" : "Oracle 数据库用户名和/或密码无效", "PostgreSQL username and/or password not valid" : "PostgreSQL 数据库用户名和/或密码无效", "You need to enter details of an existing account." : "您需要输入现有帐户的详细信息。", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X 不被支持并且 %s 在这个平台上无法正常工作。请自行承担风险!", "For the best results, please consider using a GNU/Linux server instead." : "为了达到最好的效果,请考虑使用 GNU/Linux 服务器。", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "看起来这个 %s 实例运行在32位PHP环境中并且已在php.ini中配置open_basedir。这将在文件超过4GB时出现问题,我们极力反对这样做。", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "请删除php.ini中的open_basedir设置或切换到64位PHP。", "Set an admin username." : "请设置一个管理员用户名。", "Set an admin password." : "请设置一个管理员密码。", "Can't create or write into the data directory %s" : "无法创建或写入数据目录 %s", "Invalid Federated Cloud ID" : "无效的联合云ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "分享 %s 失败, 因为后端不允许分享 %i 类型", "Sharing %s failed, because the file does not exist" : "分享 %s 失败, 因为文件不存在.", "You are not allowed to share %s" : "您无权分享 %s", "Sharing %s failed, because you can not share with yourself" : "分享 %s 失败, 因为您不能分享给自己", "Sharing %s failed, because the user %s does not exist" : "分享 %s 失败, 因为用户 %s 不存在", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "分享 %s 失败, 因为用户 %s 不是 %s 所属的任何组的用户", "Sharing %s failed, because this item is already shared with %s" : "分享 %s 失败, 因为该项已经分享给用户 %s", "Sharing %s failed, because this item is already shared with user %s" : "分享 %s 失败, 因为该项已经分享给用户 %s", "Sharing %s failed, because the group %s does not exist" : "分享 %s 失败, 因为 %s 分组不存在", "Sharing %s failed, because %s is not a member of the group %s" : "分享 %s 失败, 因为 %s 不是 %s 分组的成员", "You need to provide a password to create a public link, only protected links are allowed" : "链接分享需要密码, 您需要提供一个密码以创建公开连接", "Sharing %s failed, because sharing with links is not allowed" : "分享 %s 失败, 因为不允许使用链接分享", "Not allowed to create a federated share with the same user" : "不能给你自己分享文件", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "分享 %s 失败, 无法找到 %s, 该服务当前无法连接.", "Share type %s is not valid for %s" : "%s 不是 %s 的合法共享类型", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "无法设置过期时间. 过期时间不能晚于其分享时间 %s", "Cannot set expiration date. Expiration date is in the past" : "无法设置过期时间. 过期时间不能为过去", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "分享后端 %s 必须实现 OCP\\Share_Backend 接口", "Sharing backend %s not found" : "%s 的分享后端未找到", "Sharing backend for %s not found" : "%s 的分享后端未找到", "Sharing failed, because the user %s is the original sharer" : "分享失败, 因为用户 %s 是原始的分享者.", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "分享 %s 失败, 因为权限超过了 %s 的已有权限", "Sharing %s failed, because resharing is not allowed" : "分享 %s 失败, 因为不允许二次共享", "Sharing %s failed, because the sharing backend for %s could not find its source" : "分享 %s 失败, 因为无法找到 %s 分享后端的来源", "Sharing %s failed, because the file could not be found in the file cache" : "分享 %s 失败, 因为文件缓存中找不到该文件", "Can’t increase permissions of %s" : "无法增加%s的权限。", "Files can’t be shared with delete permissions" : "无法分享有删除权限的文件", "Files can’t be shared with create permissions" : "无法分享有创建权限的文件", "Expiration date is in the past" : "到期日期已过.", "Can’t set expiration date more than %s days in the future" : "无法将过期日期设置为超过 %s 天.", "%s shared »%s« with you" : "%s 向您分享了 »%s«", "%s shared »%s« with you." : "%s 已与您共享了 %s .", "Click the button below to open it." : "点击下方按钮可打开它.", "Open »%s«" : "打开 %s", "%s via %s" : "%s 通过 %s", "The requested share does not exist anymore" : "当前请求的共享已经不存在", "Could not find category \"%s\"" : "无法找到分类 \"%s\"", "Sunday" : "星期日", "Monday" : "星期一", "Tuesday" : "星期二", "Wednesday" : "星期三", "Thursday" : "星期四", "Friday" : "星期五", "Saturday" : "星期六", "Sun." : "周日", "Mon." : "周一", "Tue." : "周二", "Wed." : "周三", "Thu." : "周四", "Fri." : "周五", "Sat." : "周六", "Su" : "日", "Mo" : "一", "Tu" : "二", "We" : "三", "Th" : "四", "Fr" : "五", "Sa" : "六", "January" : "一月", "February" : "二月", "March" : "三月", "April" : "四月", "May" : "五月", "June" : "六月", "July" : "七月", "August" : "八月", "September" : "九月", "October" : "十月", "November" : "十一月", "December" : "十二月", "Jan." : "一月", "Feb." : "二月", "Mar." : "三月", "Apr." : "四月", "May." : "五月", "Jun." : "六月", "Jul." : "七月", "Aug." : "八月", "Sep." : "九月", "Oct." : "十月", "Nov." : "十一月", "Dec." : "十二月", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "在用户名中只允许使用以下字符:“a-z”,“A-Z”,“0-9”和\"_.@-'\"", "A valid username must be provided" : "必须提供合法的用户名", "Username contains whitespace at the beginning or at the end" : "用户名在开头或结尾处包含空格", "Username must not consist of dots only" : "用户名不能仅由点组成", "A valid password must be provided" : "必须提供合法的密码", "The username is already being used" : "用户名已被使用", "Could not create user" : "无法创建用户", "User disabled" : "用户已禁用", "Login canceled by app" : "已通过应用取消登录", "No app name specified" : "没有指定的 App 名称", "App '%s' could not be installed!" : "应用程序 '%s' 无法被安装!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "应用程序 \"%s\" 无法被安装,因为为满足下列依赖关系: %s", "a safe home for all your data" : "给您所有的数据一个安全的家", "File is currently busy, please try again later" : "文件当前正忙,请稍后再试", "Can't read file" : "无法读取文件", "Application is not enabled" : "应用程序未启用", "Authentication error" : "认证出错", "Token expired. Please reload page." : "Token 过期,请刷新页面。", "Unknown user" : "未知用户", "No database drivers (sqlite, mysql, or postgresql) installed." : "没有安装数据库驱动 (SQLite、MySQL 或 PostgreSQL)。", "Cannot write into \"config\" directory" : "无法写入“config”目录", "Cannot write into \"apps\" directory" : "无法写入“apps”目录", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "这个通常可以通过赋予 apps 目录写入权限或者在 config 文件中关闭 AppStore 来修复。详情:%s", "Cannot create \"data\" directory" : "无法创建“data”目录 ", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "这个通常可以通过赋予根目录写入权限来修复。查看:%s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "权限通常可以通过赋予根目录写入权限来修复。查看:%s。", "Setting locale to %s failed" : "设置语言为 %s 失败", "Please install one of these locales on your system and restart your webserver." : "请在您的系统中安装下述一种语言并重启 Web 服务器.", "Please ask your server administrator to install the module." : "请联系服务器管理员安装模块.", "PHP module %s not installed." : "PHP %s 模块未安装.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP 选项 \"%s\" 未设置为 \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "在 php.ini 中调整该设置将导致 Nextcloud 重新运行", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload 当前设置为 \"%s\", 预期值为 \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "请在 php.ini 中设置 <code>mbstring.func_overload</code> 为 <code>0</code> 以解决该问题", "libxml2 2.7.0 is at least required. Currently %s is installed." : "至少需要 libxml2 2.7.0. 当前安装 %s.", "To fix this issue update your libxml2 version and restart your web server." : "升级您的 libxml2 版本然后重启 Web 服务器以解决该问题.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP 被设置为移除内联块, 这将导致多个核心应用无法访问.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "这可能由缓存/加速器导致的, 例如 Zend OPcache 或 eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP 模块已经安装, 但仍然显示未安装?", "Please ask your server administrator to restart the web server." : "请联系服务器管理员重启 Web 服务器.", "PostgreSQL >= 9 required" : "要求 PostgreSQL >= 9", "Please upgrade your database version" : "请升级您的数据库版本", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "请更改权限为 0770 以避免其他用户查看目录.", "Your data directory is readable by other users" : "你的数据目录可被其他用户读取", "Your data directory must be an absolute path" : "您的数据目录必须是绝对路径", "Check the value of \"datadirectory\" in your configuration" : "请检查配置文件中 \"datadirectory\" 的值", "Your data directory is invalid" : "您的数据目录无效", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "请确定在根目录下有一个名为\".ocdata\"的文件。", "Could not obtain lock type %d on \"%s\"." : "无法在 \"%s\" 上获取锁类型 %d.", "Storage unauthorized. %s" : "存储认证失败. %s", "Storage incomplete configuration. %s" : "存储未完成配置. %s", "Storage connection error. %s" : "存储连接错误. %s", "Storage is temporarily not available" : "存储暂时不可用", "Storage connection timeout. %s" : "存储连接超时. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "您可以由 %s 设置 Web 服务器对 config 目录 %s 的写权限修复这个问题", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID 为 %s 的模块不存在. 请在应用设置中启用或联系您的管理员.", "Server settings" : "服务器设置", "DB Error: \"%s\"" : "数据库错误:\"%s\"", "Offending command was: \"%s\"" : "冲突命令为:\"%s\"", "You need to enter either an existing account or the administrator." : "你需要输入一个数据库中已有的账户或管理员账户。", "Offending command was: \"%s\", name: %s, password: %s" : "冲突命令为:\"%s\",名称:%s,密码:%s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "设置 %s 权限失败,因为权限超出了 %s 已有权限。", "Setting permissions for %s failed, because the item was not found" : "设置 %s 的权限失败,因为未找到到对应项", "Cannot clear expiration date. Shares are required to have an expiration date." : "无法清除过期时间. 每个分享必须有一个过期时间", "Cannot increase permissions of %s" : "无法提升 %s 的权限", "Files can't be shared with delete permissions" : "无法分享有删除权限的文件", "Files can't be shared with create permissions" : "无法分享有创建权限的文件", "Cannot set expiration date more than %s days in the future" : "无法将过期日期设置为超过 %s 天.", "Personal" : "个人", "Admin" : "管理", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "您可以由 %s 设置 Web 服务器对应用目录 %s 的写权限或在配置文件中禁用应用商店可以修复这个问题.", "Cannot create \"data\" directory (%s)" : "无法创建“apps”目录 (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "点击 <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">设置 Web 服务器对根目录的写入权限</a> 可修复这个问题.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "您可以由 %s 设置 Web 服务器对根目录 %s 的写权限可以修复这个问题.", "Data directory (%s) is readable by other users" : "数据目录 (%s) 能被其他用户读取", "Data directory (%s) must be an absolute path" : "数据目录 (%s) 必须为绝对路径", "Data directory (%s) is invalid" : "数据目录 (%s) 无效", "Please check that the data directory contains a file \".ocdata\" in its root." : "请检查根目录下 data 目录中包含名为 \".ocdata\" 的文件." },"pluralForm" :"nplurals=1; plural=0;" } l10n/de_DE.json 0000604 00000057466 15247130447 0007176 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Das Schreiben in das „config“-Verzeichnis ist nicht möglich!", "This can usually be fixed by giving the webserver write access to the config directory" : "Dies kann normalerweise repariert werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird", "See %s" : "Siehe %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Die Dateien der App %$1s wurden nicht korrekt ersetzt. Stellen Sie sicher, dass die Version mit dem Server kompatibel ist.", "Sample configuration detected" : "Beispielkonfiguration gefunden", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Es wurde festgestellt, dass die Beispielkonfiguration kopiert wurde. Dies kann Ihre Installation zerstören und wird nicht unterstützt. Bitte die Dokumentation lesen, bevor Änderungen an der config.php vorgenommen werden.", "%1$s and %2$s" : "%1$s und %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s und %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s und %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s und %5$s", "Education Edition" : "Bildungsausgabe", "Enterprise bundle" : "Firmen-Paket", "Groupware bundle" : "Groupware-Paket", "Social sharing bundle" : "Paket für das Teilen in sozialen Medien", "PHP %s or higher is required." : "PHP %s oder höher wird benötigt.", "PHP with a version lower than %s is required." : "PHP wird in einer früheren Version als %s benötigt.", "%sbit or higher PHP required." : "%sbit oder höheres PHP wird benötigt.", "Following databases are supported: %s" : "Die folgenden Datenbanken werden unterstützt: %s", "The command line tool %s could not be found" : "Das Kommandozeilenwerkzeug %s konnte nicht gefunden werden", "The library %s is not available." : "Die Bibliothek %s ist nicht verfügbar.", "Library %s with a version higher than %s is required - available version %s." : "Die Bibliothek %s wird in einer neueren Version als %s benötigt - verfügbare Version ist %s.", "Library %s with a version lower than %s is required - available version %s." : "Die Bibliothek %s wird in einer früheren Version als %s benötigt - verfügbare Version ist %s.", "Following platforms are supported: %s" : "Die folgenden Plattformen werden unterstützt: %s", "Server version %s or higher is required." : "Server Version %s oder höher wird benötigt.", "Server version %s or lower is required." : "Server Version %s oder niedriger wird benötigt.", "Unknown filetype" : "Unbekannter Dateityp", "Invalid image" : "Ungültiges Bild", "Avatar image is not square" : "Benutzerbild ist nicht quadratisch", "today" : "Heute", "yesterday" : "Gestern", "_%n day ago_::_%n days ago_" : ["Vor %n Tag","Vor %n Tagen"], "last month" : "Letzten Monat", "_%n month ago_::_%n months ago_" : ["Vor %n Monat","Vor %n Monaten"], "last year" : "Letztes Jahr", "_%n year ago_::_%n years ago_" : ["Vor %n Jahr","Vor %n Jahren"], "_%n hour ago_::_%n hours ago_" : ["Vor %n Stunde","Vor %n Stunden"], "_%n minute ago_::_%n minutes ago_" : ["Vor %n Minute","Vor %n Minuten"], "seconds ago" : "Gerade eben", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte aktiviere es in deinen Einstellungen oder kontaktiere deinen Administrator.", "File name is a reserved word" : "Der Dateiname ist ein reserviertes Wort", "File name contains at least one invalid character" : "Der Dateiname enthält mindestens ein ungültiges Zeichen", "File name is too long" : "Dateiname ist zu lang", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", "Empty filename is not allowed" : "Ein leerer Dateiname ist nicht erlaubt", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Die Anwendung \"%s\" kann nicht installiert werden, weil die Anwendungsinfodatei nicht gelesen werden kann.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Die App \"%s\" kann nicht installiert werden, da sie mit dieser Serverversion nicht kompatibel ist.", "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.", "Help" : "Hilfe", "Apps" : "Apps", "Settings" : "Einstellungen", "Log out" : "Abmelden", "Users" : "Benutzer", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Grundeinstellungen", "Sharing" : "Teilen", "Security" : "Sicherheit", "Encryption" : "Verschlüsselung", "Additional settings" : "Zusätzliche Einstellungen", "Tips & tricks" : "Tipps & Tricks", "Personal info" : "Persönliche Informationen ", "Sync clients" : " Sync-Clients ", "Unlimited" : "Unbegrenzt", "__language_name__" : " Deutsch (Förmlich: Sie) ", "Verifying" : "Überprüfe", "Verifying …" : " Überprüfe… ", "Verify" : "Überprüfen", "%s enter the database username and name." : "%s geben Sie den Datenbank-Benutzernamen und den Datenbanknamen an.", "%s enter the database username." : "%s geben Sie den Datenbank-Benutzernamen an.", "%s enter the database name." : "%s geben Sie den Datenbanknamen an.", "%s you may not use dots in the database name" : "%s Der Datenbankname darf keine Punkte enthalten", "Oracle connection could not be established" : "Es konnte keine Verbindung zur Oracle-Datenbank hergestellt werden", "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "You need to enter details of an existing account." : "Sie müssen Details von einem existierenden Benutzer einfügen.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", "For the best results, please consider using a GNU/Linux server instead." : "Zur Gewährleistung eines optimalen Betriebs sollte stattdessen ein GNU/Linux-Server verwendet werden.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Es scheint, dass diese %s-Instanz unter einer 32-Bit-PHP-Umgebung läuft und open_basedir in der Datei php.ini konfiguriert worden ist. Von einem solchen Betrieb wird dringend abgeraten, weil es dabei zu Problemen mit Dateien kommt, deren Größe 4 GB übersteigt.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Bitte entfernen Sie die open_basedir-Einstellung in Ihrer php.ini oder wechseln Sie zu 64-Bit-PHP.", "Set an admin username." : "Einen Administrator-Benutzernamen setzen.", "Set an admin password." : "Ein Administrator-Passwort setzen.", "Can't create or write into the data directory %s" : "Das Datenverzeichnis %s kann nicht erstellt oder es kann darin nicht geschrieben werden.", "Invalid Federated Cloud ID" : "Ungültige Federated-Cloud-ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Freigabe von %s fehlgeschlagen, da das Backend die Freigabe vom Typ %i nicht erlaubt.", "Sharing %s failed, because the file does not exist" : "Freigabe von %s fehlgeschlagen, da die Datei nicht existiert", "You are not allowed to share %s" : "Die Freigabe von %s ist Ihnen nicht erlaubt", "Sharing %s failed, because you can not share with yourself" : "Freigabe von %s fehlgeschlagen, da das Teilen mit sich selbst nicht möglich ist", "Sharing %s failed, because the user %s does not exist" : "Freigabe von %s fehlgeschlagen, da der Benutzer %s nicht existiert", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Freigabe von %s fehlgeschlagen, da der Benutzer %s kein Gruppenmitglied einer der Gruppen von %s ist", "Sharing %s failed, because this item is already shared with %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit %s geteilt wird", "Sharing %s failed, because this item is already shared with user %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit dem Benutzer %s geteilt wird", "Sharing %s failed, because the group %s does not exist" : "Freigabe von %s fehlgeschlagen, da die Gruppe %s nicht existiert", "Sharing %s failed, because %s is not a member of the group %s" : "Freigabe von %s fehlgeschlagen, da %s kein Mitglied der Gruppe %s ist", "You need to provide a password to create a public link, only protected links are allowed" : "Es sind nur geschützte Links zulässig, daher müssen Sie ein Passwort angeben, um einen öffentlichen Link zu generieren", "Sharing %s failed, because sharing with links is not allowed" : "Freigabe von %s fehlgeschlagen, da das Teilen von Verknüpfungen nicht erlaubt ist", "Not allowed to create a federated share with the same user" : "Das Erstellen einer Federated-Cloud-Freigabe mit dem gleichen Benutzer ist nicht erlaubt", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Freigabe von %s fehlgeschlagen, da %s nicht gefunden wurde. Möglicherweise ist der Server nicht erreichbar.", "Share type %s is not valid for %s" : "Freigabetyp %s ist nicht gültig für %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Ablaufdatum kann nicht gesetzt werden. Freigaben können nach dem Teilen, nicht länger als %s gültig sein.", "Cannot set expiration date. Expiration date is in the past" : "Ablaufdatum kann nicht gesetzt werden. Ablaufdatum liegt in der Vergangenheit.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Freigabe-Backend %s muss in der OCP\\Share_Backend - Schnittstelle implementiert werden", "Sharing backend %s not found" : "Freigabe-Backend %s nicht gefunden", "Sharing backend for %s not found" : "Freigabe-Backend für %s nicht gefunden", "Sharing failed, because the user %s is the original sharer" : "Freigabe fehlgeschlagen, da der Benutzer %s der ursprünglich Teilende ist", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Freigabe von %s fehlgeschlagen, da die Berechtigungen die erteilten Berechtigungen %s überschreiten", "Sharing %s failed, because resharing is not allowed" : "Freigabe von %s fehlgeschlagen, da das nochmalige Freigeben einer Freigabe nicht erlaubt ist", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Freigabe von %s fehlgeschlagen, da das Freigabe-Backend für %s nicht in dieser Quelle gefunden werden konnte", "Sharing %s failed, because the file could not be found in the file cache" : "Freigabe von %s fehlgeschlagen, da die Datei im Datei-Cache nicht gefunden werden konnte", "Can’t increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen", "Files can’t be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden", "Files can’t be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden", "Expiration date is in the past" : "Das Ablaufdatum liegt in der Vergangenheit.", "Can’t set expiration date more than %s days in the future" : "Das Ablaufdatum kann nicht mehr als %s Tage in die Zukunft liegen", "%s shared »%s« with you" : "%s hat „%s“ mit Ihnen geteilt", "%s shared »%s« with you." : "%s hat mit Ihnen »%s« geteilt.", "Click the button below to open it." : "Klicken Sie zum Öffnen auf die untere Schaltfläche.", "Open »%s«" : "»%s« öffnen", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Die angeforderte Freigabe existiert nicht mehr", "Could not find category \"%s\"" : "Die Kategorie \"%s“ konnte nicht gefunden werden", "Sunday" : "Sonntag", "Monday" : "Montag", "Tuesday" : "Dienstag", "Wednesday" : "Mittwoch", "Thursday" : "Donnerstag", "Friday" : "Freitag", "Saturday" : "Samstag", "Sun." : "Son.", "Mon." : "Mon.", "Tue." : "Die.", "Wed." : "Mit.", "Thu." : "Don.", "Fri." : "Fre.", "Sat." : "Sam.", "Su" : "So", "Mo" : "Mo", "Tu" : "Di", "We" : "Mi", "Th" : "Do", "Fr" : "Fr", "Sa" : "Sa", "January" : "Januar", "February" : "Februar", "March" : "März", "April" : "April", "May" : "Mai", "June" : "Juni", "July" : "Juli", "August" : "August", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "Dezember", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mär.", "Apr." : "Apr.", "May." : "Mai", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dez.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Folgende Zeichen sind im Benutzernamen erlaubt: „a-z“, „A-Z“, „0-9“ und „_.@-'“", "A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden", "Username contains whitespace at the beginning or at the end" : "Der Benutzername enthält Leerzeichen am Anfang oder am Ende", "Username must not consist of dots only" : "Der Benutzername darf nicht nur aus Punkten bestehen", "A valid password must be provided" : "Es muss ein gültiges Passwort eingegeben werden", "The username is already being used" : "Dieser Benutzername existiert bereits", "Could not create user" : "Benutzer konnte nicht erstellt werden", "User disabled" : "Nutzer deaktiviert", "Login canceled by app" : "Anmeldung durch die App abgebrochen", "No app name specified" : "Es wurde kein App-Name angegeben", "App '%s' could not be installed!" : "'%s' - App konnte nicht installiert werden!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Die App „%s“ kann nicht installiert werden, da die folgenden Abhängigkeiten nicht erfüllt sind: %s", "a safe home for all your data" : "ein sicherer Ort für all Ihre Daten", "File is currently busy, please try again later" : "Die Datei ist in Benutzung, bitte versuchen Sie es später noch einmal", "Can't read file" : "Datei kann nicht gelesen werden", "Application is not enabled" : "Die Anwendung ist nicht aktiviert", "Authentication error" : "Authentifizierungsfehler", "Token expired. Please reload page." : "Token abgelaufen. Bitte laden Sie die Seite neu.", "Unknown user" : "Unbekannter Benutzer", "No database drivers (sqlite, mysql, or postgresql) installed." : "Keine Datenbanktreiber (SQLite, MySQL oder PostgreSQL) installiert.", "Cannot write into \"config\" directory" : "Schreiben in das „config“-Verzeichnis ist nicht möglich", "Cannot write into \"apps\" directory" : "Schreiben in das „apps“-Verzeichnis ist nicht möglich", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das App-Verzeichnis eingeräumt wird. Siehe auch %s", "Cannot create \"data\" directory" : "Kann das \"Daten\"-Verzeichnis nicht erstellen", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Berechtigungen können zumeist korrigiert werden indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s. ", "Setting locale to %s failed" : "Das Setzen der Umgebungslokale auf %s ist fehlgeschlagen", "Please install one of these locales on your system and restart your webserver." : "Bitte installieren Sie eine dieser Sprachen auf Ihrem System und starten Sie den Webserver neu.", "Please ask your server administrator to install the module." : "Bitte kontaktieren Sie Ihren Server-Administrator und bitten Sie um die Installation des Moduls.", "PHP module %s not installed." : "PHP-Modul %s nicht installiert.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-Einstellung „%s“ ist nicht auf „%s“ gesetzt.", "Adjusting this setting in php.ini will make Nextcloud run again" : "Eine Änderung dieser Einstellung in der php.ini kann Ihre Nextcloud wieder lauffähig machen.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload ist nicht auf den erwarteten Wert „0“, sondern stattdessen auf „%s“ gesetzt", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bitte setzen Sie zum Beheben dieses Problems <code>mbstring.func_overload</code> in Ihrer php.ini auf <code>0</code>.", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 ist mindestestens erforderlich. Im Moment ist %s installiert.", "To fix this issue update your libxml2 version and restart your web server." : "Um den Fehler zu beheben, müssen Sie die libxml2 Version aktualisieren und den Webserver neustarten.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ist offenbar so konfiguriert, dass PHPDoc-Blöcke in der Anweisung entfernt werden. Dadurch sind mehrere Kern-Apps nicht erreichbar.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dies wird wahrscheinlich durch Zwischenspeicher/Beschleuniger wie etwa Zend OPcache oder eAccelerator verursacht.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-Module wurden installiert, werden aber als noch fehlend gelistet?", "Please ask your server administrator to restart the web server." : "Bitte kontaktieren Sie Ihren Server-Administrator und bitten Sie um den Neustart des Webservers.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 benötigt", "Please upgrade your database version" : "Bitte aktualisieren Sie Ihre Datenbankversion", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Bitte ändern Sie die Berechtigungen auf 0770, so dass das Verzeichnis nicht von anderen Benutzern angezeigt werden kann.", "Your data directory is readable by other users" : "Ihr Datenverzeichnis kann von anderen Benutzern gelesen werden", "Your data directory must be an absolute path" : "Ihr Datenverzeichnis muss einen eindeutigen Pfad haben", "Check the value of \"datadirectory\" in your configuration" : "Überprüfen Sie bitte die Angabe unter „datadirectory“ in Ihrer Konfiguration", "Your data directory is invalid" : "Dein Datenverzeichnis ist ungültig.", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Stellen Sie sicher, dass eine Datei \".ocdata\" im Wurzelverzeichnis des data-Verzeichnisses existiert.", "Could not obtain lock type %d on \"%s\"." : "Sperrtyp %d auf „%s“ konnte nicht ermittelt werden.", "Storage unauthorized. %s" : "Speicher nicht authorisiert. %s", "Storage incomplete configuration. %s" : "Speicher-Konfiguration unvollständig. %s", "Storage connection error. %s" : "Verbindungsfehler zum Speicherplatz. %s", "Storage is temporarily not available" : "Speicher ist vorübergehend nicht verfügbar", "Storage connection timeout. %s" : "Zeitüberschreitung der Verbindung zum Speicherplatz. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Dies kann normalerweise behoben werden, %sindem dem Webserver Schreibzugriff auf das Konfigurationsverzeichnis gegeben wird %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte aktivieren Sie es in Ihren App-Einstellungen oder kontaktieren Sie Ihren Administrator.", "Server settings" : "Servereinstellungen", "DB Error: \"%s\"" : "DB-Fehler: „%s“", "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", "You need to enter either an existing account or the administrator." : "Sie müssen entweder ein existierendes Benutzerkonto oder das Administratorenkonto angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Das Setzen der Berechtigungen für %s ist fehlgeschlagen, da die neuen Berechtigungen, die erteilten Berechtigungen %s überschreiten", "Setting permissions for %s failed, because the item was not found" : "Das Setzen der Berechtigungen für %s ist fehlgeschlagen, da das Element nicht gefunden wurde", "Cannot clear expiration date. Shares are required to have an expiration date." : "Ablaufdatum kann nicht gelöscht werden. Freigaben werden für ein Ablaufdatum benötigt.", "Cannot increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen", "Files can't be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden", "Files can't be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden", "Cannot set expiration date more than %s days in the future" : "Das Ablaufdatum kann nicht mehr als %s Tage in die Zukunft liegen", "Personal" : "Persönlich", "Admin" : "Verwaltung", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Dies kann normalerweise behoben werden, %sindem dem Webserver Schreibzugriff auf das App-Verzeichnis gegeben wird%s oder der App Store in der Konfigurationsdatei deaktiviert wird.", "Cannot create \"data\" directory (%s)" : "Erstellen des „data“-Verzeichnisses ist nicht möglich (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Dies kann normalerweise repariert werden, indem dem Webserver <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> Schreibzugriff auf das Wurzelverzeichnis gegeben wird</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Berechtigungen können normalerweise repariert werden, indem dem Webserver %s Schreibzugriff auf das Wurzelverzeichnis %s gegeben wird.", "Data directory (%s) is readable by other users" : "Datenverzeichnis (%s) ist von anderen Benutzern lesbar", "Data directory (%s) must be an absolute path" : "Das Datenverzeichnis (%s) muss ein absoluter Pfad sein", "Data directory (%s) is invalid" : "Datenverzeichnis (%s) ist ungültig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Bitte stellen Sie sicher, dass das Datenverzeichnis auf seiner ersten Ebene eine Datei namens „.ocdata“ enthält." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/ja.js 0000604 00000062117 15247130447 0006260 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "\"config\"ディレクトリに書き込めません!", "This can usually be fixed by giving the webserver write access to the config directory" : "多くの場合、これはWebサーバーにconfigディレクトリへの書き込み権限を与えることで解決できます。", "See %s" : "%s を閲覧", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "多くの場合、Webサーバーの configディレクトリ に書き込み権限を与えることで直ります。%s を見てください", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "アプリ %1$s のファイルが正しく置き換えられませんでした。サーバーと互換性のあるバージョンであることを確認してください。", "Sample configuration detected" : "サンプル設定が見つかりました。", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "サンプル設定がコピーされてそのままです。このままではインストールが失敗し、サポート対象外になります。config.phpを変更する前にドキュメントを確認してください。", "%1$s and %2$s" : "%1$s と %2$s", "%1$s, %2$s and %3$s" : "%1$s と %2$s、%3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s と %2$s、%3$s、%4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s と %2$s、%3$s、%4$s、%5$s", "Education Edition" : "Education Edition", "PHP %s or higher is required." : "PHP %s 以上が必要です。", "PHP with a version lower than %s is required." : "%s 以前のバージョンのPHPが必要です。", "%sbit or higher PHP required." : "%sbit 以上の新しいバージョンのPHPが必要です。", "Following databases are supported: %s" : "次のデータベースをサポートしています: %s", "The command line tool %s could not be found" : "コマンド '%s' は見つかりませんでした。", "The library %s is not available." : " %s ライブラリーが利用できません。", "Library %s with a version higher than %s is required - available version %s." : "%s ライブラリーは、%s よりも新しいバージョンが必要です。利用可能なバージョンは、 %s です。", "Library %s with a version lower than %s is required - available version %s." : "%s ライブラリーは、%s よりも古いバージョンが必要です。利用可能なバージョンは、 %s です。", "Following platforms are supported: %s" : "次のプラットフォームをサポートしています: %s", "Server version %s or higher is required." : "サーバーの %s よりも高いバージョンが必要です。", "Server version %s or lower is required." : "サーバーの %s よりも低いバージョンが必要です。", "Unknown filetype" : "不明なファイルタイプ", "Invalid image" : "無効な画像", "Avatar image is not square" : "アバター画像が正方形ではありません", "today" : "今日", "yesterday" : "1日前", "_%n day ago_::_%n days ago_" : ["%n 日前"], "last month" : "1ヶ月前", "_%n month ago_::_%n months ago_" : ["%nヶ月前"], "last year" : "1年前", "_%n year ago_::_%n years ago_" : ["%n 年前"], "_%n hour ago_::_%n hours ago_" : ["%n 時間前"], "_%n minute ago_::_%n minutes ago_" : ["%n 分前"], "seconds ago" : "数秒前", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID: %sのモジュールは存在しません。アプリ設定で有効にするか、管理者に問い合わせてください。", "File name is a reserved word" : "ファイル名が予約された単語です", "File name contains at least one invalid character" : "ファイル名に1文字以上の無効な文字が含まれています", "File name is too long" : "ファイル名が長すぎます", "Dot files are not allowed" : "ドットファイルは許可されていません", "Empty filename is not allowed" : "空のファイル名は許可されていません", "App \"%s\" cannot be installed because appinfo file cannot be read." : "appinfoファイルが読み込めないため、アプリ名 \"%s\" がインストールできません。", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "\"%s\" アプリは、このバージョンのサーバーと互換性がないためインストールされませんでした。", "This is an automatically sent email, please do not reply." : "これは自動的に生成されたメールです。返信しないでください。", "Help" : "ヘルプ", "Apps" : "アプリ", "Settings" : "設定", "Log out" : "ログアウト", "Users" : "ユーザー", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "基本設定", "Sharing" : "共有", "Security" : "セキュリティ", "Encryption" : "暗号化", "Additional settings" : "追加設定", "Tips & tricks" : "ヒントとコツ", "Personal info" : "個人情報", "Sync clients" : "同期クライアント", "Unlimited" : "無制限", "__language_name__" : "Japanese (日本語)", "Verifying" : "検証中", "Verifying …" : "検証中", "Verify" : "検証", "%s enter the database username and name." : "%s データベース名とデータベースのユーザー名を入力してください。", "%s enter the database username." : "%s のデータベースのユーザー名を入力してください。", "%s enter the database name." : "%s のデータベース名を入力してください。", "%s you may not use dots in the database name" : "%s ではデータベース名にドットを利用できないかもしれません。", "Oracle connection could not be established" : "Oracleへの接続が確立できませんでした。", "Oracle username and/or password not valid" : "Oracleのユーザー名もしくはパスワードは有効ではありません", "PostgreSQL username and/or password not valid" : "PostgreSQLのユーザー名もしくはパスワードは有効ではありません", "You need to enter details of an existing account." : "既存のアカウントの詳細を入力してください。", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X では、サポートされていません。このOSでは、%sは正常に動作しないかもしれません。ご自身の責任においてご利用ください。", "For the best results, please consider using a GNU/Linux server instead." : "最も良い方法としては、代わりにGNU/Linuxサーバーを利用することをご検討ください。", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "このインスタンス %s は、32bit PHP 環境で動作しており、php.ini に open_basedir が設定されているようです。4GB以上のファイルで問題が発生するため、この設定を利用しないことをお勧めします。", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "php.ini から open_basedir 設定を削除するか、64bit PHPに切り替えてください。", "Set an admin username." : "管理者のユーザー名を設定", "Set an admin password." : "管理者のパスワードを設定", "Can't create or write into the data directory %s" : "%s データディレクトリに作成、書き込みができません", "Invalid Federated Cloud ID" : "無効な統合されたクラウドID", "Sharing %s failed, because the backend does not allow shares from type %i" : "%s を共有できませんでした。%i タイプからの共有は許可されていません。", "Sharing %s failed, because the file does not exist" : "%s を共有できませんでした。そのファイルは存在しません。", "You are not allowed to share %s" : "%s を共有することを許可されていません。", "Sharing %s failed, because you can not share with yourself" : "%s を共有できませんでした。自分自身に共有することはできません。", "Sharing %s failed, because the user %s does not exist" : "%s を共有できませんでした。ユーザー %s が存在しません。", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s を共有できませんでした。ユーザー %s はどのグループにも属していません。%s は、??のメンバーです。", "Sharing %s failed, because this item is already shared with %s" : "%s を共有できませんでした。このアイテムはすでに %s に共有されています。", "Sharing %s failed, because this item is already shared with user %s" : "%s を共有できませんでした。このアイテムは、ユーザー %s によりすでに共有されています。", "Sharing %s failed, because the group %s does not exist" : "%s を共有できませんでした。グループ %s は存在しません。", "Sharing %s failed, because %s is not a member of the group %s" : "%s を共有できませんでした。%s は、グループ %s のメンバーではありません。", "You need to provide a password to create a public link, only protected links are allowed" : "公開用リンクの作成にはパスワードの設定が必要です", "Sharing %s failed, because sharing with links is not allowed" : "%s を共有できませんでした。リンクでの共有は許可されていません。", "Not allowed to create a federated share with the same user" : "同じユーザーでフェデレーション共有を作成することは出来ません", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "%s を共有できませんでした。%s が見つかりませんでした。現在サーバーに接続できないようです。", "Share type %s is not valid for %s" : "%s の共有方法は、%s には適用できません。", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "有効期限を設定できません。共有開始から %s 以降に有効期限を設定することはできません。", "Cannot set expiration date. Expiration date is in the past" : "有効期限を設定できません。有効期限が過去を示しています。", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "%s のバックエンドの共有には、OCP\\Share_Backend インターフェースを実装しなければなりません。", "Sharing backend %s not found" : "共有バックエンド %s が見つかりません", "Sharing backend for %s not found" : "%s のための共有バックエンドが見つかりません", "Sharing failed, because the user %s is the original sharer" : "共有できませんでした。ユーザー %sは元々の共有者です。", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "%s を共有できませんでした。%s に許可されている権限を越えています。", "Sharing %s failed, because resharing is not allowed" : "%s を共有できませんでした。再共有は許可されていません。", "Sharing %s failed, because the sharing backend for %s could not find its source" : "%s の共有に失敗しました。%s のバックエンド共有に必要なソースが見つかりませんでした。", "Sharing %s failed, because the file could not be found in the file cache" : "%s の共有に失敗しました。ファイルキャッシュにファイルがありませんでした。", "Can’t increase permissions of %s" : "%s の権限を追加できません", "Files can’t be shared with delete permissions" : "削除権限つきでファイルを共有できません。", "Files can’t be shared with create permissions" : "作成権限つきでファイルを共有できません。", "Expiration date is in the past" : "有効期限が切れています", "Can’t set expiration date more than %s days in the future" : "有効期限を%s日以降に設定できません。", "%s shared »%s« with you" : "%sが あなたと »%s«を共有しました", "%s shared »%s« with you." : "%sが あなたと »%s«を共有しました", "Click the button below to open it." : "開くには下のボタンをクリック", "Open »%s«" : "»%s«を開く", "%s via %s" : "%s に %s から", "The requested share does not exist anymore" : "この共有はもう存在しません。", "Could not find category \"%s\"" : "カテゴリ \"%s\" が見つかりませんでした", "Sunday" : "日曜日", "Monday" : "月曜日", "Tuesday" : "火曜日", "Wednesday" : "水曜日", "Thursday" : "木曜日", "Friday" : "金曜日", "Saturday" : "土曜日", "Sun." : "日", "Mon." : "月", "Tue." : "火", "Wed." : "水", "Thu." : "木", "Fri." : "金", "Sat." : "土", "Su" : "日", "Mo" : "月", "Tu" : "火", "We" : "水", "Th" : "木", "Fr" : "金", "Sa" : "土", "January" : "1月", "February" : "2月", "March" : "3月", "April" : "4月", "May" : "5月", "June" : "6月", "July" : "7月", "August" : "8月", "September" : "9月", "October" : "10月", "November" : "11月", "December" : "12月", "Jan." : "1月", "Feb." : "2月", "Mar." : "3月", "Apr." : "4月", "May." : "5月", "Jun." : "6月", "Jul." : "7月", "Aug." : "8月", "Sep." : "9月", "Oct." : "10月", "Nov." : "11月", "Dec." : "12月", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "ユーザー名で利用できる文字列は、次のものです: \"a-z\", \"A-Z\", \"0-9\", \"_.@-\"", "A valid username must be provided" : "有効なユーザー名を指定する必要があります", "Username contains whitespace at the beginning or at the end" : "ユーザー名の最初か最後に空白が含まれています", "Username must not consist of dots only" : "ユーザー名は、ドットのみではつけられません", "A valid password must be provided" : "有効なパスワードを指定する必要があります", "The username is already being used" : "ユーザー名はすでに使われています", "User disabled" : "ユーザーは無効です", "Login canceled by app" : "アプリによりログインが中止されました", "No app name specified" : "アプリ名が未指定", "App '%s' could not be installed!" : "'%s' アプリをインストールできませんでした。", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "次の依存関係が満たされないため、\"%s\" アプリをインストールできません:%s", "a safe home for all your data" : "あなたの全データの安全な家", "File is currently busy, please try again later" : "現在ファイルはビジーです。後でもう一度試してください。", "Can't read file" : "ファイルを読み込めません", "Application is not enabled" : "アプリケーションは無効です", "Authentication error" : "認証エラー", "Token expired. Please reload page." : "トークンが無効になりました。ページを再読込してください。", "Unknown user" : "不明なユーザー", "No database drivers (sqlite, mysql, or postgresql) installed." : "データベースドライバー (sqlite, mysql, postgresql) がインストールされていません。", "Cannot write into \"config\" directory" : "\"config\" ディレクトリに書き込みができません", "Cannot write into \"apps\" directory" : "\"apps\" ディレクトリに書き込みができません", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "多くの場合、これは Webサーバーにappsディレクトリへの書き込み権限を与えるか、設定ファイルでアプリストアを無効化することで直ります。%s を見てください。", "Cannot create \"data\" directory" : "\"data\" ディレクトリを作成できません", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "多くの場合、Webサーバーのルートディレクトリに書き込み権限を与えることで直ります。%s を見てください。", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Webサーバーのルートディレクトリに書き込み権限パーミッションが必要です。%s を見てください。", "Setting locale to %s failed" : "ロケールを %s に設定できませんでした", "Please install one of these locales on your system and restart your webserver." : "これらのロケールのうちいずれかをシステムにインストールし、Webサーバーを再起動してください。", "Please ask your server administrator to install the module." : "サーバー管理者にモジュールのインストールを依頼してください。", "PHP module %s not installed." : "PHP のモジュール %s がインストールされていません。", "PHP setting \"%s\" is not set to \"%s\"." : "PHP設定の\"%s\"は \"%s\"に設定されていません", "Adjusting this setting in php.ini will make Nextcloud run again" : "php.ini のこの設定を調整して、再度 Nextcloudを起動してください。", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload の値は \"0\" であるべきですが、\"%s\" に設定されています", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "この問題を修正するには、php.ini ファイルの <code>mbstring.func_overload</code> を <code>0</code> に設定してください。", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 バージョン 2.7.0 が最低必要です。現在 %s がインストールされています。", "To fix this issue update your libxml2 version and restart your web server." : "この問題を解決するには、libxml2 を更新して、ウェブサーバーを再起動してください。", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHPでインラインドキュメントブロックを取り除く設定になっています。これによりコアアプリで利用できないものがいくつかあります。", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "これは、Zend OPcacheやeAccelerator 等のキャッシュ/アクセラレーターが原因かもしれません。", "PHP modules have been installed, but they are still listed as missing?" : "PHP モジュールはインストールされていますが、まだ一覧に表示されていますか?", "Please ask your server administrator to restart the web server." : "サーバー管理者にWebサーバーを再起動するよう依頼してください。", "PostgreSQL >= 9 required" : "PostgreSQL 9以上が必要です", "Please upgrade your database version" : "新しいバージョンのデータベースにアップグレードしてください", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "ディレクトリが他のユーザーから見えないように、パーミッションを 0770 に変更してください。", "Your data directory is readable by other users" : "データディレクトリは、他のユーザーから読み取り専用です", "Your data directory must be an absolute path" : "データディレクトリは、絶対パスにする必要があります", "Check the value of \"datadirectory\" in your configuration" : "設定ファイル内の \"datadirectory\" の値を確認してください。", "Your data directory is invalid" : "データディレクトリが無効です", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "データディレクトリの直下に \".ocdata\" ファイルがあるのを確認してください。", "Could not obtain lock type %d on \"%s\"." : "\"%s\" で %d タイプのロックを取得できませんでした。", "Storage unauthorized. %s" : "権限のないストレージです。 %s", "Storage incomplete configuration. %s" : "設定が未完了のストレージです。 %s", "Storage connection error. %s" : "ストレージへの接続エラー。 %s", "Storage is temporarily not available" : "ストレージは一時的に利用できません", "Storage connection timeout. %s" : "ストレージへの接続がタイムアウト。 %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "多くの場合、これは %s Webサーバーにconfigディレクトリ %s への書き込み権限を与えることで解決できます。", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "id: %sのモジュールは存在しません。アプリ設定で有効にするか、管理者に問い合わせてください。", "Server settings" : "サーバー設定", "DB Error: \"%s\"" : "DBエラー: \"%s\"", "Offending command was: \"%s\"" : "違反コマンド: \"%s\"", "You need to enter either an existing account or the administrator." : "既存のアカウントもしくは管理者のどちらかを入力する必要があります。", "Offending command was: \"%s\", name: %s, password: %s" : "違反コマンド: \"%s\"、名前: %s、パスワード: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "%s を共有できませんでした。%s に許可されている権限を越えています。", "Setting permissions for %s failed, because the item was not found" : "%s を共有できませんでした。アイテムが存在しません。", "Cannot clear expiration date. Shares are required to have an expiration date." : "有効期限を解除できません。共有するには有効期限を設定する必要があります。", "Cannot increase permissions of %s" : "%s の権限を強化できません", "Files can't be shared with delete permissions" : "削除権限つきでファイルを共有できません。", "Files can't be shared with create permissions" : "作成権限つきでファイルを共有できません。", "Cannot set expiration date more than %s days in the future" : "有効期限を%s日以降に設定できません。", "Personal" : "個人", "Admin" : "管理", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "多くの場合、これは %s Webサーバーにappsディレクトリ %s への書き込み権限を与えるか、設定ファイルでアプリストアを無効化することで解決できます。", "Cannot create \"data\" directory (%s)" : "\"data\" ディレクトリ (%s) を作成できません", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "通常、<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">Webサーバーにルートディレクトリへの書き込み権限を与える</a>ことで解決できます。", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "多くの場合、パーミッションは %s Webサーバーにルートディレクトリ %s への書き込み権限を与えることで解決できます。", "Data directory (%s) is readable by other users" : "データディレクトリ (%s) は他のユーザーも閲覧することができます", "Data directory (%s) must be an absolute path" : "データディレクトリ (%s) は、絶対パスである必要があります。", "Data directory (%s) is invalid" : "データディレクトリ (%s) は無効です", "Please check that the data directory contains a file \".ocdata\" in its root." : "データディレクトリに \".ocdata\" ファイルが含まれていることを確認してください。" }, "nplurals=1; plural=0;"); l10n/sr.json 0000604 00000072412 15247130447 0006646 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Не могу да уписујем у „config“ директоријум!", "This can usually be fixed by giving the webserver write access to the config directory" : "Ово се обично може средити давањем права веб серверу да пише у директоријум са подешавањима", "See %s" : "Погледајте %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ово се обично може средити давањем права писања веб серверу за директоријум са подешавањима. Погледајте %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Фајлови апликације „%$1s“ нису правилно замењени. Проверите да ли је верзија компатибилна са сервером.", "Sample configuration detected" : "Откривен је пример подешавања", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Откривено је да је прекопиран пример подешавања. Ово може покварити инсталацију и није подржано. Прочитајте документацију пре вршења промена у фајлу config.php", "%1$s and %2$s" : "%1$s и %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s и %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s и %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s и %5$s", "Education Edition" : "Образовно издање", "Enterprise bundle" : "Комплет за предузећа", "Groupware bundle" : "Комплет за радне тимове", "Social sharing bundle" : "Комплет за друштвене мреже", "PHP %s or higher is required." : "Потребан је PHP %s или новији.", "PHP with a version lower than %s is required." : "Потребна је PHP верзија старија од верзије %s.", "%sbit or higher PHP required." : "Потребна је верзија PHP-а једнака или већа од верзије %s.", "Following databases are supported: %s" : "Подржане су следеће базе података: %s", "The command line tool %s could not be found" : "Алатку командне линије „%s“ није могуће пронаћи", "The library %s is not available." : "Библиотека „%s“ није доступна.", "Library %s with a version higher than %s is required - available version %s." : "Потребна је библиотека „%s“ верзије веће од %s - доступна верзија је %s.", "Library %s with a version lower than %s is required - available version %s." : "Потребна је библиотека „%s“ верзије ниже од %s - доступна верзија је %s.", "Following platforms are supported: %s" : "Подржане су следеће платформе: %s", "Server version %s or higher is required." : "Потребна је верзија сервера %s или виша.", "Server version %s or lower is required." : "Потребна је верзија сервера %s или нижа.", "Unknown filetype" : "Непознат тип фајла", "Invalid image" : "Неисправна слика", "Avatar image is not square" : "Слика аватара није квадратна", "today" : "данас", "yesterday" : "јуче", "_%n day ago_::_%n days ago_" : ["пре %n дан","пре %n дана","пре %n дана"], "last month" : "прошлог месеца", "_%n month ago_::_%n months ago_" : ["пре %n месец","пре %n месеца","пре %n месеци"], "last year" : "прошле године", "_%n year ago_::_%n years ago_" : ["пре %n годину","пре %n године","пре %n година"], "_%n hour ago_::_%n hours ago_" : ["пре %n сат","пре %n сата","пре %n сати"], "_%n minute ago_::_%n minutes ago_" : ["пре %n минут","пре %n минута","пре %n минута"], "seconds ago" : "пре неколико секунди", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модул са идентификацијом: %s не постоји. Омогућите га у подешавањима апликација или контактирајте администратора.", "File name is a reserved word" : "Назив фајла је резервисана реч", "File name contains at least one invalid character" : "Назив фајла садржи бар један недозвољен знак", "File name is too long" : "Назив фајла је предугачак", "Dot files are not allowed" : "Фајлови са почетном тачком нису дозвољени", "Empty filename is not allowed" : "Празан назив није дозвољен", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Апликација \"%s\" не може бити инсталирана јер appinfo фајл не може да се прочита.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Апликација \"%s\" не може бити инсталирана јер није компатибилна са овом верзијом сервера.", "This is an automatically sent email, please do not reply." : "Ово је аутоматски генерисана порука, не одговарајте на њу.", "Help" : "Помоћ", "Apps" : "Апликације", "Settings" : "Поставке", "Log out" : "Одјава", "Users" : "Корисници", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Основне поставке", "Sharing" : "Дељење", "Security" : "Безбедност", "Encryption" : "Шифровање", "Additional settings" : "Додатне поставке", "Tips & tricks" : "Савети и трикови", "Personal info" : "Лични подаци", "Sync clients" : "Клијенти у синхронизацији", "Unlimited" : "Неограничено", "__language_name__" : "Српски", "Verifying" : "Проверавам", "Verifying …" : "Проверавам ...", "Verify" : "Провери", "%s enter the database username and name." : "%s унеси корисничко име базе података и име.", "%s enter the database username." : "%s унеси корисничко име базе података.", "%s enter the database name." : "%s унеси име базе података.", "%s you may not use dots in the database name" : "%s не можете користити тачке у имену базе података", "Oracle connection could not be established" : "Веза са базом података Oracle не може бити успостављена", "Oracle username and/or password not valid" : "Oracle корисничко име и/или лозинка нису исправни", "PostgreSQL username and/or password not valid" : "PostgreSQL корисничко име и/или лозинка нису исправни", "You need to enter details of an existing account." : "Потребно је да унесете детаље постојећег налога.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Мек ОС Икс није подржан и %s неће радити исправно на овој платформи. Користите га на сопствени ризик!", "For the best results, please consider using a GNU/Linux server instead." : "За најбоље резултате, размотрите употребу ГНУ/Линукс сервера.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Изгледа да %s ради у 32-битном PHP окружењу а open_basedir је подешен у php.ini фајлу. То може довести до проблема са фајловима већим од 4 GB, те стога није препоручљиво.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Уклоните open_basedir поставку из php.ini фајла или пређите на 64-битни PHP.", "Set an admin username." : "Поставите име за администратора.", "Set an admin password." : "Поставите лозинку за администратора.", "Can't create or write into the data directory %s" : "Не могу креирати или уписивати у директоријум података %s", "Invalid Federated Cloud ID" : "Неисправан ИД Здруженог облака", "Sharing %s failed, because the backend does not allow shares from type %i" : "Дељење %s није успело зато што позадина не дозвољава дељење од типа %i", "Sharing %s failed, because the file does not exist" : "Дељење %s није успело зато што фајл не постоји", "You are not allowed to share %s" : "Није вам дозвољено да делите %s", "Sharing %s failed, because you can not share with yourself" : "Дељење %s није успело зато што не можете да делите са самим собом", "Sharing %s failed, because the user %s does not exist" : "Дељење %s није успело зато што не постоји корисник %s", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Дељење %s није успело зато што корисник %s није члан ниједне групе чији је %s члан", "Sharing %s failed, because this item is already shared with %s" : "Дељење %s није успело зато што се ова ставка већ дели са %s", "Sharing %s failed, because this item is already shared with user %s" : "Дељење %s није успело зато што се ова ставка већ дели са корисником %s", "Sharing %s failed, because the group %s does not exist" : "Дељење %s није успело зато што не постоји група %s", "Sharing %s failed, because %s is not a member of the group %s" : "Дељење %s није успело зато што %s није члан групе %s", "You need to provide a password to create a public link, only protected links are allowed" : "Морате да обезбедите лозинку за креирање јавне везе, дозвољене су само заштићене везе", "Sharing %s failed, because sharing with links is not allowed" : "Дељење %s није успело зато што дељење са везама није дозвољено", "Not allowed to create a federated share with the same user" : "Није дозвољено да направите здружено дељење са истим корисником", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Дељење %s није успело, није могуће пронаћи %s, можда сервер тренутно није доступан.", "Share type %s is not valid for %s" : "Тип фајла за дељење %s није исправан за %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Не могу поставити датум трајања. Дељења не могу истицати касније од %s пошто су активирана", "Cannot set expiration date. Expiration date is in the past" : "Не могу поставити датум трајања. Датум трајања употребе је у прошлости", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Позадина дељења %s мора користити корисничко окружење OCP\\Share_Backend", "Sharing backend %s not found" : "Позадина за дељење %s није пронађена", "Sharing backend for %s not found" : "Позадина за дељење за %s није пронађена", "Sharing failed, because the user %s is the original sharer" : "Дељење није успело, зато што је корисник %s већ оригинални делилац", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Дељење %s није успело зато што дозволе превазилазе дозволе гарантоване за %s", "Sharing %s failed, because resharing is not allowed" : "Дељење %s није успело зато што даље дељење није дозвољено", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Дељење %s није успело зато што позадина дељења за %s није могла да нађе извор", "Sharing %s failed, because the file could not be found in the file cache" : "Дељење %s није успело зато што фајл није нађен у кешу фајлова", "Can’t increase permissions of %s" : "Не могу да повећам дозволе за %s", "Files can’t be shared with delete permissions" : "Фајлови не могу бити дељени са дозволама за брисање", "Files can’t be shared with create permissions" : "Фајлови не могу бити дељени са дозволама за креирање", "Expiration date is in the past" : "Датум истека је у прошлости", "Can’t set expiration date more than %s days in the future" : "Не могу да поставим датум истека више од %s дана у будућност", "%s shared »%s« with you" : "%s подели „%s“ са вама", "%s shared »%s« with you." : "%s подели „%s“ са вама.", "Click the button below to open it." : "Кликните дугме испод да га отворите.", "Open »%s«" : "Отвори „%s“", "%s via %s" : "%s путем %s", "The requested share does not exist anymore" : "Захтевано дељење више не постоји", "Could not find category \"%s\"" : "Не могу да пронађем категорију „%s“.", "Sunday" : "Недеља", "Monday" : "Понедељак", "Tuesday" : "Уторак", "Wednesday" : "Среда", "Thursday" : "Четвртак", "Friday" : "Петак", "Saturday" : "Субота", "Sun." : "Нед", "Mon." : "Пон", "Tue." : "Уто", "Wed." : "Сре", "Thu." : "Чет", "Fri." : "Пет", "Sat." : "Суб", "Su" : "Не", "Mo" : "По", "Tu" : "Ут", "We" : "Ср", "Th" : "Че", "Fr" : "Пе", "Sa" : "Су", "January" : "Јануар", "February" : "Фебруар", "March" : "Март", "April" : "Април", "May" : "Мај", "June" : "Јун", "July" : "Јул", "August" : "Август", "September" : "Септембар", "October" : "Октобар", "November" : "Новембар", "December" : "Децембар", "Jan." : "Јан.", "Feb." : "Феб.", "Mar." : "Мар.", "Apr." : "Апр.", "May." : "Мај.", "Jun." : "Јун.", "Jul." : "Јул.", "Aug." : "Авг.", "Sep." : "Сеп.", "Oct." : "Окт.", "Nov." : "Нов.", "Dec." : "Дец.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "У корисничком имену су дозвољени само следећи карактери: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Морате унети исправно корисничко име", "Username contains whitespace at the beginning or at the end" : "Корисничко име садржи белине на почетку или на крају", "Username must not consist of dots only" : "Корисничко име не могу бити само тачке", "A valid password must be provided" : "Морате унети исправну лозинку", "The username is already being used" : "Корисничко име се већ користи", "Could not create user" : "Не могу да направим корисника", "User disabled" : "Корисник онемогућен", "Login canceled by app" : "Пријава отказана од стране апликације", "No app name specified" : "Није наведен назив апликације", "App '%s' could not be installed!" : "Апликација '%s' не може да се инсталира!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Апликација „%s“ не може бити инсталирана јер следеће зависности нису испуњене: %s", "a safe home for all your data" : "сигурно место за све Ваше податке", "File is currently busy, please try again later" : "Фајл је тренутно заузет, покушајте поново касније", "Can't read file" : "Не могу да читам фајл", "Application is not enabled" : "Апликација није укључена", "Authentication error" : "Грешка при провери идентитета", "Token expired. Please reload page." : "Жетон је истекао. Поново учитајте страницу.", "Unknown user" : "Непознат корисник", "No database drivers (sqlite, mysql, or postgresql) installed." : "Нема драјвера базе података (скулајт, мајскул или постгрескул).", "Cannot write into \"config\" directory" : "Не могу уписивати у директоријуму „config“", "Cannot write into \"apps\" directory" : "Не могу уписивати у директоријуму „apps“", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Ово се обично може поправити тако што веб серверу дате приступ уписа за директоријум где су апликације или тако што онемогућите продавницу у config фајлу. Видети %s", "Cannot create \"data\" directory" : "Не могу да направим \"data\" директоријум", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Ово се обично може поправити тако што веб серверу дате право уписа за корени директоријуму. Видети %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Привилегије се обично могу поправити тако што веб серверу дате право уписа за корени директоријуму. Видети %s", "Setting locale to %s failed" : "Постављање локалитета на %s није успело", "Please install one of these locales on your system and restart your webserver." : "Инсталирајте неки од ових локалитета на ваш систем и поново покрените веб сервер.", "Please ask your server administrator to install the module." : "Замолите администратора вашег сервера да инсталира тај модул.", "PHP module %s not installed." : "PHP модул %s није инсталиран.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP поставка „%s“ није постављена на „%s“.", "Adjusting this setting in php.ini will make Nextcloud run again" : "Некстклауд ће прорадити поново када прилагодите ово подашавање у php.ini фајлу", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload је постављено на „%s“ уместо на очекивану вредност „0“", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Да би решили овај проблем поставите <code>mbstring.func_overload</code> на <code>0</code> у фајлу php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Потребан је бар libxml2 2.7.0. Тренутно је инсталиран %s.", "To fix this issue update your libxml2 version and restart your web server." : "Да поправите овај проблем, ажурирајте верзију библиотеке libxml2 и рестартујте веб сервер.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP је очигледно подешен да склања уметнуте doc блокове. То ће учинити неколико кључних апликација недоступним.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Ово је вероватно изазвано кешом или акцелератором као што су ЗендОПкеш или еАкцелератор.", "PHP modules have been installed, but they are still listed as missing?" : "PHP модули су инсталирани али се и даље воде као недостајући?", "Please ask your server administrator to restart the web server." : "Замолите вашег администратора сервера да поново покрене веб сервер.", "PostgreSQL >= 9 required" : "Захтеван је ПостгреСкул >= 9", "Please upgrade your database version" : "Надоградите ваше издање базе", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Промените дозволе у 0770 како директоријуми не би могли бити излистани од стране других корисника.", "Your data directory is readable by other users" : "Директоријум са подацима је читљив од стране других корисника система", "Your data directory must be an absolute path" : "Директоријум са подацима мора бити апсолутна путања", "Check the value of \"datadirectory\" in your configuration" : "Проверите податак за \"datadirectory\" у вашој конфигурацији", "Your data directory is invalid" : "Директоријум са подацима није исправан", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Уверите се да фајл \".ocdata\" постоји у корену директоријума са подацима.", "Could not obtain lock type %d on \"%s\"." : "Не могу да остварим закључаност %d за „%s“.", "Storage unauthorized. %s" : "Складиште није овлашћено. %s", "Storage incomplete configuration. %s" : "Непотпуна конфигурација складишта. %s", "Storage connection error. %s" : "Грешка приликом повезивања на складиште. %s", "Storage is temporarily not available" : "Складиште привремено није доступно", "Storage connection timeout. %s" : "Прекорачено време за повезивање на складиште. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Ово се обично може средити %sдавањем права веб серверу да пише у директоријум са подешавањима%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модул са ИД %s не постоји. Укључите га у поставкама апликација или контактирајте администратора.", "Server settings" : "Подешавања сервера", "DB Error: \"%s\"" : "Грешка базе података: \"%s\"", "Offending command was: \"%s\"" : "Неисправна команда је: „%s“", "You need to enter either an existing account or the administrator." : "Потребно је да унесете или постојећи налог или администраторски.", "Offending command was: \"%s\", name: %s, password: %s" : "Неисправна команда је: „%s“, назив: %s, лозинка: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Постављање дозвола за %s није успело зато што дозволе превазилазе дозволе гарантоване за %s", "Setting permissions for %s failed, because the item was not found" : "Постављање дозвола за %s није успело зато што ставка није пронађена", "Cannot clear expiration date. Shares are required to have an expiration date." : "Не могу обрисати датум трајања. Дељења су у обавези да имају ограничен датум трајања.", "Cannot increase permissions of %s" : "Не могу да повећам привилегије за %s", "Files can't be shared with delete permissions" : "Фајлови не могу бити дељени са привилегијама за брисање", "Files can't be shared with create permissions" : "Фајлови не могу бити дељени са привилегијама за прављење", "Cannot set expiration date more than %s days in the future" : "Датум истека не може да се постави више од %s дана у будућност", "Personal" : "Лично", "Admin" : "Администрација", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Ово се обично може поправити %sgдавањем права уписа веб серверу директоријум%s апликација или искуључивањем продавнице апликација у фајлу config file.", "Cannot create \"data\" directory (%s)" : "Не могу формирати \"data\" директоријуме (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Ово је обично може поправити <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">давајући веб серверу право писања у корени директоријум</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Дозволе се обично могу поправити %sдавањем права уписивања веб серверу основни директоријум%s.", "Data directory (%s) is readable by other users" : "Директоријум података (%s) могу читати остали корисници", "Data directory (%s) must be an absolute path" : "Директоријум података (%s) мора бити апсолутна путања", "Data directory (%s) is invalid" : "Директоријум података (%s) није исправан", "Please check that the data directory contains a file \".ocdata\" in its root." : "Проверите да ли директоријум података садржи фајл „.ocdata“ у свом основном директоријуму." },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" } l10n/es_MX.json 0000604 00000057237 15247130447 0007245 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "¡No se puede escribir en el directorio \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Esto generalmente se resuelve dándole al servidor web acceso para escribir en el directorio config. ", "See %s" : "Ver %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio config. Por favor ve %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Los archivos de la aplicación %$1s no fueron correctamente remplazados. Por favor asegúrarte de que la versión sea compatible con el servidor.", "Sample configuration detected" : "Se ha detectado la configuración de muestra", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Se ha detectado que la configuración de muestra ha sido copiada. Esto puede arruiniar tu instalacón y no está soportado. Por favor lee la documentación antes de hacer cambios en el archivo config.php", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s y %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s y %5$s", "Education Edition" : "Edición Educativa", "Enterprise bundle" : "Paquete empresarial", "Groupware bundle" : "Paquete de Groupware", "Social sharing bundle" : "Paquete para compartir en redes sociales", "PHP %s or higher is required." : "Se requiere de PHP %s o superior.", "PHP with a version lower than %s is required." : "PHP con una versión inferiror a la %s es requerido. ", "%sbit or higher PHP required." : "se requiere PHP para %sbit o superior.", "Following databases are supported: %s" : "Las siguientes bases de datos están soportadas: %s", "The command line tool %s could not be found" : "No fue posible encontar la herramienta de línea de comando %s", "The library %s is not available." : "La biblioteca %s no está disponible. ", "Library %s with a version higher than %s is required - available version %s." : "La biblitoteca %s con una versión superiror a la %s es requerida - versión disponible %s.", "Library %s with a version lower than %s is required - available version %s." : "Se requiere de la biblioteca %s con una versión inferiror a la %s - la versión %s está disponible. ", "Following platforms are supported: %s" : "Las siguientes plataformas están soportadas: %s", "Server version %s or higher is required." : "Se requiere la versión del servidor %s o superior. ", "Server version %s or lower is required." : "La versión del servidor %s o inferior es requerdia. ", "Unknown filetype" : "Tipo de archivo desconocido", "Invalid image" : "Imagen inválida", "Avatar image is not square" : "La imagen del avatar no es un cuadrado", "today" : "hoy", "yesterday" : "ayer", "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"], "last month" : "mes pasado", "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"], "last year" : "año pasado", "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"], "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"], "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"], "seconds ago" : "hace segundos", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ", "File name is a reserved word" : "Nombre de archivo es una palabra reservada", "File name contains at least one invalid character" : "El nombre del archivo contiene al menos un caracter inválido", "File name is too long" : "El nombre del archivo es demasiado largo", "Dot files are not allowed" : "Los archivos Dot no están permitidos", "Empty filename is not allowed" : "El uso de nombres de archivo vacíos no está permitido", "App \"%s\" cannot be installed because appinfo file cannot be read." : "La aplicación \"%s\" no puede ser instalada porque el archivo appinfo no se puede leer. ", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "La aplicación \"%s\" no puede ser instalada porque no es compatible con esta versión del servidor. ", "This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, por favor no lo contestes. ", "Help" : "Ayuda", "Apps" : "Aplicaciones", "Settings" : "Configuraciones", "Log out" : "Salir", "Users" : "Usuarios", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Configuraciones básicas", "Sharing" : "Compartiendo", "Security" : "Seguridad", "Encryption" : "Encripción", "Additional settings" : "Configuraciones adicionales", "Tips & tricks" : "Consejos & trucos", "Personal info" : "Información personal", "Sync clients" : "Sincronizar clientes", "Unlimited" : "Ilimitado", "__language_name__" : "Español (México)", "Verifying" : "Verficando", "Verifying …" : "Verficando ...", "Verify" : "Verificar", "%s enter the database username and name." : "%s ingresa el usuario y nombre de la base de datos", "%s enter the database username." : "%s ingresa el nombre de usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s no puedes utilizar puntos en el nombre de la base de datos", "Oracle connection could not be established" : "No fue posible establecer la conexión a Oracle", "Oracle username and/or password not valid" : "Usuario y/o contraseña de Oracle inválidos", "PostgreSQL username and/or password not valid" : "El Usuario y/o Contraseña de PostgreSQL inválido(s)", "You need to enter details of an existing account." : "Necesitas ingresar los detalles de una cuenta existente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "OS X de Mac no está soportado y %s no funcionará correctamente en esta plataforma ¡Úsalo bajo tu propio riesgo!", "For the best results, please consider using a GNU/Linux server instead." : "Para mejores resultados, por favor cosidera usar en su lugar un servidor GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Al parecer esta instancia %s está corriendo en un ambiente PHP de 32-bits y el open_basedir ha sido configurado en el archivo php.ini. Esto generará problemas con archivos de más de 4GB de tamaño y es altamente desalentado. ", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor elimina el ajuste open_basedir de tu archivo php.ini o cambia a PHP de 64 bits. ", "Set an admin username." : "Establecer un Usuario administrador", "Set an admin password." : "Establecer la contraseña del administrador.", "Can't create or write into the data directory %s" : "No es posible crear o escribir en el directorio de datos %s", "Invalid Federated Cloud ID" : "ID Inválido", "Sharing %s failed, because the backend does not allow shares from type %i" : "Se presentó una falla al compartir %s, porque el backend no permite elementos compartidos de tipo %i", "Sharing %s failed, because the file does not exist" : "Se presentó una falla al compartir %s porque el archivo no existe", "You are not allowed to share %s" : "No tienes permitido compartir %s", "Sharing %s failed, because you can not share with yourself" : "Se presentó una falla al compartir %s, porque no puedes compartir contigo mismo", "Sharing %s failed, because the user %s does not exist" : "Se presentó una falla al compartir %s porque el usuario %s no existe", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Se presentó una falla al compartir %s porque el usuario %s no es un miembro de ninguno de los grupos de los cuales %s es miembro", "Sharing %s failed, because this item is already shared with %s" : "Se presentó una falla al compartir %s, porque este elemento ya había sido compartido con %s", "Sharing %s failed, because this item is already shared with user %s" : "Se presento una falla al compartir %s, porque este elemento ya ha sido compartido con el usuario %s", "Sharing %s failed, because the group %s does not exist" : "Se presentó una falla al compartir %s, porque el grupo %s no existe", "Sharing %s failed, because %s is not a member of the group %s" : "Se presentó una falla al compartir %s debido a que %s no es un miembro del grupo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Necesitas proporcionar una contraseña para crear una liga pública, sólo se permiten ligas protegidas. ", "Sharing %s failed, because sharing with links is not allowed" : "Se presentó una falla al compartir %s porque no está permitido compartir con ligas", "Not allowed to create a federated share with the same user" : "No está permitido crear un elemento compartido con el mismo usuario", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Se presentó una falla al compartir %s, no fue posible encontrar %s, tal vez el servidor sea inalcanzable por el momento", "Share type %s is not valid for %s" : "El tipo del elemento compartido %s no es válido para %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "No ha sido posible establecer la fecha de expiración. Los recursos compartidos no pueden expirar después de %s tras haber sido compartidos", "Cannot set expiration date. Expiration date is in the past" : "No ha sido posible establecer la fecha de expiración. La fecha de expiración ya ha pasado", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El backend %s que comparte debe implementar la interface OCP\\Share_Backend", "Sharing backend %s not found" : "No fue encontrado el Backend que comparte %s ", "Sharing backend for %s not found" : "No fue encontrado el Backend que comparte para %s", "Sharing failed, because the user %s is the original sharer" : "Se presentó una falla al compartir, porque el usuario %s es quien compartió originalmente", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Se presentó una falla al compartir %s, porque los permisos exceden los permisos otorgados a %s", "Sharing %s failed, because resharing is not allowed" : "Falla al compartir %s debído a que no se permite volver a compartir", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Se presentó una falla al compartir %s porque el backend que comparte %s no pudo encontrar su origen", "Sharing %s failed, because the file could not be found in the file cache" : "Se presentó una falla al compartir %s porque el archivo no se encontró en el caché de archivos", "Can’t increase permissions of %s" : "No es posible incrementar los privilegios de %s", "Files can’t be shared with delete permissions" : "Los archivos no se pueden compartir con permisos de borrado", "Files can’t be shared with create permissions" : "Los archivos no se pueden compartir con permisos de creación", "Expiration date is in the past" : "La fecha de expiración se encuentra en el pasado", "Can’t set expiration date more than %s days in the future" : "No es posible establecer la fecha de expiración más allá de %s días en el futuro", "%s shared »%s« with you" : "%s ha compartido »%s« contigo", "%s shared »%s« with you." : "%s compartió contigo »%s«.", "Click the button below to open it." : "Haz click en el botón inferior para abrirlo. ", "Open »%s«" : "Abrir »%s«", "%s via %s" : "%s por %s", "The requested share does not exist anymore" : "El recurso compartido solicitado ya no existe", "Could not find category \"%s\"" : "No fue posible encontrar la categoria \"%s\"", "Sunday" : "Domingo", "Monday" : "Lunes", "Tuesday" : "Martes", "Wednesday" : "Miércoles", "Thursday" : "Jueves", "Friday" : "Viernes", "Saturday" : "Sábado", "Sun." : "Dom.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mie.", "Thu." : "Jue.", "Fri." : "Vie.", "Sat." : "Sab.", "Su" : "Do", "Mo" : "Lu", "Tu" : "Ma", "We" : "Mi", "Th" : "Ju", "Fr" : "Vi", "Sa" : "Sa", "January" : "Enero", "February" : "Febrero", "March" : "Marzo", "April" : "Abril", "May" : "Mayo", "June" : "Junio", "July" : "Julio", "August" : "Agosto", "September" : "Septiembre", "October" : "Octubre", "November" : "Noviembre", "December" : "Diciembre", "Jan." : "Ene.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "May.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Ago.", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Dic.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Sólo se permiten los siguientes caracteres en el usuario: \"a-z\", \"A-Z\", \"0-9\" y \"_.@-'\"", "A valid username must be provided" : "Debes proporcionar un nombre de usuario válido", "Username contains whitespace at the beginning or at the end" : "El usuario contiene un espacio en blanco al inicio o al final", "Username must not consist of dots only" : "El usuario no debe consistir de solo puntos. ", "A valid password must be provided" : "Se debe proporcionar una contraseña válida", "The username is already being used" : "Ese usuario ya está en uso", "Could not create user" : "No fue posible crear el usuario", "User disabled" : "Usuario deshabilitado", "Login canceled by app" : "Inicio de sesión cancelado por la aplicación", "No app name specified" : "No se ha especificado el nombre de la aplicación", "App '%s' could not be installed!" : "¡La aplicación \"%s\" no pudo ser instalada!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "La aplicación \"%s\" no puede ser instalada porque las siguientes dependencias no están satisfechas: %s ", "a safe home for all your data" : "un lugar seguro para todos tus datos", "File is currently busy, please try again later" : "El archivo se encuentra actualmente en uso, por favor intentalo más tarde. ", "Can't read file" : "No se puede leer el archivo", "Application is not enabled" : "La aplicación está deshabilitada", "Authentication error" : "Error de autenticación", "Token expired. Please reload page." : "La ficha ha expirado. Por favor recarga la página.", "Unknown user" : "Ususario desconocido", "No database drivers (sqlite, mysql, or postgresql) installed." : "No cuentas con controladores de base de datos (sqlite, mysql o postgresql) instalados. ", "Cannot write into \"config\" directory" : "No fue posible escribir en el directorio \"config\"", "Cannot write into \"apps\" directory" : "No fue posible escribir en el directorio \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio de las aplicaciones o deshabilitando la appstore en el archivo config. Por favor ve %s", "Cannot create \"data\" directory" : "No fue posible crear el directorio \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Por lo general esto se puede resolver al darle al servidor web acceso de escritura al directorio raíz. Por favor ve %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Por lo general los permisos se pueden corregir al darle al servidor web acceso de escritura al directorio raíz. Por favor ve %s.", "Setting locale to %s failed" : "Se presentó una falla al establecer la regionalización a %s", "Please install one of these locales on your system and restart your webserver." : "Por favor instala uno de las siguientes configuraciones locales en tu sistema y reinicia tu servidor web", "Please ask your server administrator to install the module." : "Por favor solicita a tu adminsitrador la instalación del módulo. ", "PHP module %s not installed." : "El módulo de PHP %s no está instalado. ", "PHP setting \"%s\" is not set to \"%s\"." : "El ajuste PHP \"%s\" no esta establecido a \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "El cambiar este ajuste del archivo php.ini hará que Nextcloud corra de nuevo.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload está establecido como \"%s\" en lugar del valor esperado de \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Para corregir este tema, establece <code>mbstring.func_overload</code> a <code>0</code> en tu archivo php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Se requiere de por lo menos libxml2 2.7.0. Actualmente %s está instalado. ", "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este tema, por favor actualiza la versión de su libxml2 y reinicia tu servidor web. ", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Al parecer PHP está configurado para quitar los bloques de comentarios internos. Esto hará que varias aplicaciones principales sean inaccesibles. ", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Esto ha sido causado probablemente por un acelerador de caché como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "¿Los módulos de PHP han sido instalados, pero se siguen enlistando como faltantes?", "Please ask your server administrator to restart the web server." : "Por favor solicita al administrador reiniciar el servidor web. ", "PostgreSQL >= 9 required" : "Se requiere PostgreSQL >= 9", "Please upgrade your database version" : "Por favor actualiza tu versión de la base de datos", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor cambia los permisos a 0770 para que el directorio no pueda ser enlistado por otros usuarios. ", "Your data directory is readable by other users" : "Tu direcctorio data puede ser leído por otros usuarios", "Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta", "Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración", "Your data directory is invalid" : "Tu directorio de datos es inválido", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Asegurate de que exista una archivo llamado \".ocdata\" en la raíz del directorio de datos. ", "Could not obtain lock type %d on \"%s\"." : "No fue posible obtener el tipo de bloqueo %d en \"%s\". ", "Storage unauthorized. %s" : "Almacenamiento no autorizado. %s", "Storage incomplete configuration. %s" : "Configuración incompleta del almacenamiento. %s", "Storage connection error. %s" : "Se presentó un error con la conexión al almacenamiento. %s", "Storage is temporarily not available" : "El almacenamieto se encuentra temporalmente no disponible", "Storage connection timeout. %s" : "El tiempo de la conexión del almacenamiento se agotó. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Esto generalmente se soluciona %s dándole al servidor web acceso para escribir en el directorio config %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con id: %s no existe. Por favor habilítalo en tus configuraciones de aplicación o contacta a tu administrador. ", "Server settings" : "Configuraciones del servidor", "DB Error: \"%s\"" : "Error de BD: \"%s\"", "Offending command was: \"%s\"" : "El comando infractor fue: \"%s\"", "You need to enter either an existing account or the administrator." : "Necesitas ingresar una cuenta ya existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "El comando infractor fue: \"%s\", nombre: %s, contraseña: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Se presentó una falla al establecer los permisos para %s, porque los permisos exceden los permisos otorgados a %s", "Setting permissions for %s failed, because the item was not found" : "Se persentó una falla al establecer los permisos para %s, porque no se encontró el elemento ", "Cannot clear expiration date. Shares are required to have an expiration date." : "No ha sido posible borrar la fecha de expiración. Los elelentos compartidos deben tener una fecha de expiración.", "Cannot increase permissions of %s" : "No se pueden incrementar los permisos de %s", "Files can't be shared with delete permissions" : "No es posible compartir archivos con permisos de borrado", "Files can't be shared with create permissions" : "No es posible compartir archivos con permisos de creación", "Cannot set expiration date more than %s days in the future" : "No es posible establecer la fecha de expiración más allá de %s días en el futuro", "Personal" : "Personal", "Admin" : "Administración", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Esto se puede arreglar por %s al darle acceso de escritura al servidor web al directorio de las aplicaciones %s o al deshabilitar la tienda de aplicaciones en el archivo de configuración", "Cannot create \"data\" directory (%s)" : "No fue posible crear el directorio de \"datos\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Esto se puede arreglar generalmente al <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">darle al servidor web accesos al directorio raíz</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Los permisos se pueden arreglar generalmente al %s darle al servidor web accesos al direcotiro raíz %s.", "Data directory (%s) is readable by other users" : "El directorio de datos (%s) puede ser leído por otros usuarios", "Data directory (%s) must be an absolute path" : "El directorio de datos (%s) debe ser una ruta absoluta", "Data directory (%s) is invalid" : "El directorio de datos (%s) es inválido", "Please check that the data directory contains a file \".ocdata\" in its root." : "Por favor verifica que el directorio de datos tenga un archivo \".ocdata\" en su raíz. " },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/is.json 0000604 00000055107 15247130447 0006637 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Get ekki skrifað í \"config\" möppuna!", "This can usually be fixed by giving the webserver write access to the config directory" : "Þetta er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í stillingamöppuna", "See %s" : "Skoðaðu %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Þetta er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í stillingamöppuna. Sjá %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Skrám forritsins %$1s var ekki rétt skipt út. Gakktu úr skugga um að þetta sé útgáfa sem sé samhæfð útgáfu vefþjónsins.", "Sample configuration detected" : "Fann sýnisuppsetningu", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Komið hefur í ljós að sýniuppsetningin var afrituð. Þetta getur skemmt uppsetninguna og er ekki stutt. Endilega lestu hjálparskjölin áður en þú gerir breytingar á config.php", "%1$s and %2$s" : "%1$s og %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s og %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s og %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s og %5$s", "Education Edition" : "Kennsluútgáfa", "Enterprise bundle" : "Fyrirtækjavöndull", "Groupware bundle" : "Hópvinnsluvöndull", "Social sharing bundle" : "Deilivöndull fyrir samfélagsmiðla", "PHP %s or higher is required." : "Krafist er PHP %s eða hærra.", "PHP with a version lower than %s is required." : "Krafist er PHP útgáfu %s eða lægri.", "%sbit or higher PHP required." : "Krafist er PHP %sbita eða hærra.", "Following databases are supported: %s" : "Eftirfarandi gagnagrunnar eru studdir: %s", "The command line tool %s could not be found" : "Skipanalínutólið \"%s\" fannst ekki", "The library %s is not available." : "Aðgerðasafnið %s er ekki tiltækt.", "Library %s with a version higher than %s is required - available version %s." : "Krafist er aðgerðasafns %s með útgáfu hærri en %s - tiltæk útgáfa er %s.", "Library %s with a version lower than %s is required - available version %s." : "Krafist er aðgerðasafns %s með útgáfu lægri en %s - tiltæk útgáfa er %s.", "Following platforms are supported: %s" : "Eftirfarandi stýrikerfi eru studd: %s", "Server version %s or higher is required." : "Krafist er þjóns af útgáfu %s eða hærra.", "Server version %s or lower is required." : "Krafist er þjóns af útgáfu %s eða lægri.", "Unknown filetype" : "Óþekkt skráategund", "Invalid image" : "Ógild mynd", "Avatar image is not square" : "Auðkennismynd er ekki ferningslaga", "today" : "í dag", "yesterday" : "í gær", "_%n day ago_::_%n days ago_" : ["fyrir %n degi síðan","fyrir %n dögum síðan"], "last month" : "í síðasta mánuði", "_%n month ago_::_%n months ago_" : ["fyrir %n mánuði","fyrir %n mánuðum"], "last year" : "síðasta ári", "_%n year ago_::_%n years ago_" : ["fyrir %n degi síðan","fyrir %n árum síðan"], "_%n hour ago_::_%n hours ago_" : ["fyrir %n klukkustund síðan","fyrir %n klukkustundum síðan"], "_%n minute ago_::_%n minutes ago_" : ["fyrir %n mínútu síðan","fyrir %n mínútum síðan"], "seconds ago" : "sekúndum síðan", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Eining með auðkenni: %s er ekki til. Virkjaðu hana í forritastillingum eða hafðu samband við kerfisstjóra.", "File name is a reserved word" : "Skráarheiti er þegar frátekið orð", "File name contains at least one invalid character" : "Skráarheitið inniheldur að minnsta kosti einn ógildan staf", "File name is too long" : "Skráarheiti er of langt", "Dot files are not allowed" : "Skrár með punkti eru ekki leyfðar", "Empty filename is not allowed" : "Autt skráarheiti er ekki leyft.", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Ekki er hægt að setja upp \"%s\" forritið vegna þess að ekki var hægt að lesa appinfo-skrána.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Ekki var hægt að setja upp forritið \"%s\" vegna þess að það er ekki samhæft þessari útgáfu vefþjónsins.", "This is an automatically sent email, please do not reply." : "Þetta er sjálfvirk tölvupóstsending, ekki svara þessu.", "Help" : "Hjálp", "Apps" : "Forrit", "Settings" : "Stillingar", "Log out" : "Skrá út", "Users" : "Notendur", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Grunnstillingar", "Sharing" : "Deiling", "Security" : "Öryggi", "Encryption" : "Dulritun", "Additional settings" : "Valfrjálsar stillingar", "Tips & tricks" : "Ábendingar og góð ráð", "Personal info" : "Persónulegar upplýsingar", "Sync clients" : "Samstilla biðlara", "Unlimited" : "Ótakmarkað", "__language_name__" : "Íslenska", "Verifying" : "Sannreyni", "Verifying …" : "Sannreyni …", "Verify" : "Sannreyna", "%s enter the database username and name." : "%s settu inn notandanafn og nafn á gagnagrunni.", "%s enter the database username." : "%s settu inn notandanafn í gagnagrunni.", "%s enter the database name." : "%s settu inn nafn á gagnagrunni.", "%s you may not use dots in the database name" : "%s þú mátt ekki nota punkta í nafni á gagnagrunni", "Oracle connection could not be established" : "Ekki tókst að koma tengingu á við Oracle", "Oracle username and/or password not valid" : "Notandanafn eða lykilorð Oracle er ekki gilt", "PostgreSQL username and/or password not valid" : "Notandanafn eða lykilorð PostgreSQL er ekki gilt", "You need to enter details of an existing account." : "Þú verður að setja inn auðkenni fyrirliggjandi notandaaðgangs.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X er ekki stutt og %s mun ekki vinna eðlilega á þessu stýrikerfi. Notaðu þetta því á þína eigin ábyrgð! ", "For the best results, please consider using a GNU/Linux server instead." : "Fyrir bestu útkomu ættirðu að íhuga að nota GNU/Linux þjón í staðinn.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Það lítur út eins og þessi %s uppsetning sé að keyra á 32-bita PHP umhverfi og að open_basedir hafi verið stillt í php.ini. Þetta mun valda vandamálum með skrár stærri en 4 GB og er stranglega mælt gegn því að þetta sé gert.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Fjarlægðu stillinguna open_basedir úr php.ini eða skiptu yfir í 64-bita PHP.", "Set an admin username." : "Stilltu notandanafn kerfisstjóra.", "Set an admin password." : "Stilltu lykilorð kerfisstjóra.", "Can't create or write into the data directory %s" : "Gat ekki búið til eða skrifað í gagnamöppuna %s", "Invalid Federated Cloud ID" : "Ógilt skýjasambandsauðkenni (Federated Cloud ID)", "Sharing %s failed, because the backend does not allow shares from type %i" : "Deiling %s mistókst, því bakvinnslukerfið leyfir ekki sameignir af gerðinni %i", "Sharing %s failed, because the file does not exist" : "Deiling %s mistókst, því skráin er ekki til", "You are not allowed to share %s" : "Þú hefur ekki heimild til að deila %s", "Sharing %s failed, because you can not share with yourself" : "Deiling %s mistókst, því þú getur ekki deilt með sjálfum þér", "Sharing %s failed, because the user %s does not exist" : "Deiling %s mistókst, því notandinn %s er ekki til", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Deiling %s mistókst, því notandinn %s er ekki meðlimur í neinum hópi sem %s er meðlimur í", "Sharing %s failed, because this item is already shared with %s" : "Deiling %s mistókst, því þessu atriði er þegar deilt með %s", "Sharing %s failed, because this item is already shared with user %s" : "Deiling %s mistókst, því þessu atriði er þegar deilt með notandanum %s", "Sharing %s failed, because the group %s does not exist" : "Deiling %s mistókst, því hópurinn %s er ekki til", "Sharing %s failed, because %s is not a member of the group %s" : "Deiling %s mistókst, því %s er ekki meðlimur í hópnum %s", "You need to provide a password to create a public link, only protected links are allowed" : "Þú verður að setja inn lykilorð til að útbúa opinberan tengil, aðeins verndaðir tenglar eru leyfðir", "Sharing %s failed, because sharing with links is not allowed" : "Deiling %s mistókst, því deiling með tenglum er ekki leyfð", "Not allowed to create a federated share with the same user" : "Ekki er heimilt að búa til skýjasambandssameign með sama notanda", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Deiling %s mistókst, gat ekki fundið %s, hugsanlega er þjónninn ekki tiltækur í augnablikinu.", "Share type %s is not valid for %s" : "Deiling af gerðinni %s er ekki gild fyrir %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Get ekki stillt gildistímann. Sameignir geta ekki runnið út síðar en %s eftir að þeim hefur verið deilt", "Cannot set expiration date. Expiration date is in the past" : "Get ekki stillt gildistímann. Gildistíminn er þegar runninn út", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Deilingarbakendinn %s verður að vera settur upp fyrir viðmótið OCP\\Share_Backend", "Sharing backend %s not found" : "Deilingarbakendinn %s fannst ekki", "Sharing backend for %s not found" : "Deilingarbakendi fyrir %s fannst ekki", "Sharing failed, because the user %s is the original sharer" : "Deiling mistókst, því notandinn %s er upprunalegur deilandi", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Deiling %s mistókst, því heimildirnar eru rétthærri en heimildir til handa %s", "Sharing %s failed, because resharing is not allowed" : "Deiling %s mistókst, því endurdeiling er ekki leyfð", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Deiling %s mistókst, því bakvinnslukerfið fyrir %s fann ekki upptök þess", "Sharing %s failed, because the file could not be found in the file cache" : "Deiling %s mistókst, því skráin fannst ekki í skyndiminni skráa", "Can’t increase permissions of %s" : "Get ekki aukið aðgangsheimildir %s", "Files can’t be shared with delete permissions" : "Ekki er hægt að deila skrá með eyða-heimildum", "Files can’t be shared with create permissions" : "Ekki er hægt að deila skrá með búa-til-heimildum", "Expiration date is in the past" : "Gildistíminn er þegar runninn út", "Can’t set expiration date more than %s days in the future" : "Ekki er hægt að setja lokadagsetningu meira en %s daga fram í tímann", "%s shared »%s« with you" : "%s deildi »%s« með þér", "%s shared »%s« with you." : "%s deildi »%s« með þér.", "Click the button below to open it." : "Smelltu á hnappinn hér fyrir neðan til að opna það.", "Open »%s«" : "Opna »%s«", "%s via %s" : "%s með %s", "The requested share does not exist anymore" : "Umbeðin sameign er ekki lengur til", "Could not find category \"%s\"" : "Fann ekki flokkinn \"%s\"", "Sunday" : "Sunnudagur", "Monday" : "Mánudagur", "Tuesday" : "Þriðjudagur", "Wednesday" : "Miðvikudagur", "Thursday" : "Fimmtudagur", "Friday" : "Föstudagur", "Saturday" : "Laugardagur", "Sun." : "Sun.", "Mon." : "Mán.", "Tue." : "Þri.", "Wed." : "Mið.", "Thu." : "Fim.", "Fri." : "Fös.", "Sat." : "Lau.", "Su" : "Su", "Mo" : "Má", "Tu" : "Þr", "We" : "Mi", "Th" : "Fi", "Fr" : "Fö", "Sa" : "La", "January" : "Janúar", "February" : "Febrúar", "March" : "Mars", "April" : "Apríl", "May" : "Maí", "June" : "Júní", "July" : "Júlí", "August" : "Ágúst", "September" : "September", "October" : "Október", "November" : "Nóvember", "December" : "Desember", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Maí.", "Jun." : "Jún.", "Jul." : "Júl.", "Aug." : "Ágú.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nóv.", "Dec." : "Des.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Einungis eru leyfilegir eftirfarandi stafir í notandanafni: \"a-z\", \"A-Z\", \"0-9\", og \"_.@-'\"", "A valid username must be provided" : "Skráðu inn gilt notandanafn", "Username contains whitespace at the beginning or at the end" : "Notandanafnið inniheldur orðabil í upphafi eða enda", "Username must not consist of dots only" : "Notandanafn má ekki einungis samanstanda af punktum", "A valid password must be provided" : "Skráðu inn gilt lykilorð", "The username is already being used" : "Notandanafnið er þegar í notkun", "Could not create user" : "Gat ekki búið til notanda", "User disabled" : "Notandi óvirkur", "Login canceled by app" : "Forrit hætti við innskráningu", "No app name specified" : "Ekkert heiti forrits tilgreint", "App '%s' could not be installed!" : "Ekki var hægt að setja upp '%s' forritið!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Ekki var hægt að setja upp \"%s\" forritið þar sem eftirfarandi kerfiskröfur eru ekki uppfylltar: %s", "a safe home for all your data" : "öruggur staður fyrir öll gögnin þín", "File is currently busy, please try again later" : "Skráin er upptekin í augnablikinu, reyndu aftur síðar", "Can't read file" : "Get ekki lesið skrána", "Application is not enabled" : "Forrit ekki virkt", "Authentication error" : "Villa við auðkenningu", "Token expired. Please reload page." : "Kenniteikn er útrunnið. Þú ættir að hlaða síðunni aftur inn.", "Unknown user" : "Óþekktur notandi", "No database drivers (sqlite, mysql, or postgresql) installed." : "Engir reklar fyrir gagnagrunn eru uppsettir (sqlite, mysql eða postgresql).", "Cannot write into \"config\" directory" : "Get ekki skrifað í \"config\" möppuna", "Cannot write into \"apps\" directory" : "Get ekki skrifað í \"apps\" möppuna", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Þetta er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í forritamöppuna með því að gera forritabúðina óvirka í stillingaskránni. Sjá %s", "Cannot create \"data\" directory" : "Get ekki búið til \"data\" möppu", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Þetta er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í rótarmöppuna. Sjá %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Heimildir er venjulega hægt að laga með því að gefa vefþjóninum skrifréttindi í rótarmöppuna. Sjá %s", "Setting locale to %s failed" : "Mistókst að setja upp staðfærsluna %s", "Please install one of these locales on your system and restart your webserver." : "Settu upp eina af þessum staðfærslum og endurræstu vefþjóninn.", "Please ask your server administrator to install the module." : "Biddu kerfisstjórann þinn um að setja eininguna upp.", "PHP module %s not installed." : "PHP-einingin %s er ekki uppsett.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-stillingin \"%s\" er ekki sett á \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ef þessi stilling er löguð í php.ini mun Nextcloud keyra aftur", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload er stillt á \"%s\" í stað gildisins \"0\" eins og vænst var", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Til að laga þetta vandamál ættirðu að setja <code>mbstring.func_overload</code> sem <code>0</code> í php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Krafist er libxml2 2.7.0 hið minnsta. Núna er %s uppsett.", "To fix this issue update your libxml2 version and restart your web server." : "Til að laga þetta vandamál ættirðu að uppfæra útgáfu þína af libxml2 og endurræsa vefþjóninn.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP virðist vera sett upp to fjarlægja innantextablokkir (inline doc blocks). Þetta mun gera ýmis kjarnaforrit óaðgengileg.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Þessu veldur væntanlega biðminni/hraðall á borð við Zend OPcache eða eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Búið er að setja upp PHP-einingar, en eru þær ennþá taldar upp eins og þær vanti?", "Please ask your server administrator to restart the web server." : "Biddu kerfisstjórann þinn um að endurræsa vefþjóninn.", "PostgreSQL >= 9 required" : "Krefst PostgreSQL >= 9", "Please upgrade your database version" : "Uppfærðu útgáfu gagnagrunnsins", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Endilega breyttu heimildunum í 0770 svo að aðrir notendur geti ekki listað upp innihald hennar.", "Your data directory is readable by other users" : "Gagnamappn þín er lesanleg fyrir aðra notendur", "Your data directory must be an absolute path" : "Gagnamappan þín verður að vera með algilda slóð", "Check the value of \"datadirectory\" in your configuration" : "Athugaðu gildi \"datadirectory\" í uppsetningunni þinni", "Your data directory is invalid" : "Gagnamappan þín er ógild", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Gakktu úr skugga um að til staðar sé skrá með heitinu \".ocdata\" í rót gagnageymslunnar.", "Could not obtain lock type %d on \"%s\"." : "Gat ekki fengið læsingu af gerðinni %d á \"%s\".", "Storage unauthorized. %s" : "Gagnageymsla ekki auðkennd. %s", "Storage incomplete configuration. %s" : "Ófullgerð uppsetning gagnageymslu. %s", "Storage connection error. %s" : "Villa í tengingu við gagnageymslu. %s", "Storage is temporarily not available" : "Gagnageymsla ekki tiltæk í augnablikinu", "Storage connection timeout. %s" : "Gagnageymsla féll á tíma. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Þetta er venjulega hægt að laga ef %sgefur vefþjóninum skrifréttindi í stillingamöppuna%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Eining með auðkenni: %s er ekki til. Virkjaðu hana í forritastillingum eða hafðu samband við kerfisstjóra.", "Server settings" : "Stillingar þjóns", "DB Error: \"%s\"" : "Gagnagrunnsvilla: \"%s\"", "Offending command was: \"%s\"" : "Saknæma skipunin var: \"%s\"", "You need to enter either an existing account or the administrator." : "Þú verður að setja inn fyrirliggjandi notandaaðgang eða kerfisstjóra.", "Offending command was: \"%s\", name: %s, password: %s" : "Saknæma skipunin var: \"%s\", nafn: %s, lykilorð: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Stilling heimilda fyrir %s mistókst, því heimildirnar eru rétthærri en heimildir til handa %s", "Setting permissions for %s failed, because the item was not found" : "Stilling heimilda fyrir %s mistókst, því atriðið fannst ekki", "Cannot clear expiration date. Shares are required to have an expiration date." : "Get ekki hreinsað út gildistímann. Ætlast er til þess að sameignir hafi ákveðinn gildistíma.", "Cannot increase permissions of %s" : "Get ekki aukið aðgangsheimildir %s", "Files can't be shared with delete permissions" : "Ekki er hægt að deila skrá með eyða-heimildum", "Files can't be shared with create permissions" : "Ekki er hægt að deila skrá með búa-til-heimildum", "Cannot set expiration date more than %s days in the future" : "Ekki er hægt að setja lokadagsetningu meira en %s daga fram í tímann", "Personal" : "Einka", "Admin" : "Stjórnun", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Þetta er venjulega hægt að laga ef %sgefur vefþjóninum skrifréttindi í forritamöppuna%s eða gerir forritabúðina óvirka í stillingaskránni.", "Cannot create \"data\" directory (%s)" : "Get ekki búið til \"data\" möppu (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Þetta er venjulega hægt að laga ef <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">gefur vefþjóninum skrifréttindi í rótarmöppuna </a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Heimildir er venjulega hægt að laga ef %sgefur vefþjóninum skrifréttindi í rótarmöppuna %s.", "Data directory (%s) is readable by other users" : "Gagnamappa (%s) er lesanleg fyrir aðra notendur", "Data directory (%s) must be an absolute path" : "Gagnamappan (%s) verður að vera algild slóð", "Data directory (%s) is invalid" : "Gagnamappa (%s) er ógild", "Please check that the data directory contains a file \".ocdata\" in its root." : "Athugaðu hvort gagnamappan innihaldi skrá með heitinu \".ocdata\" í rót hennar." },"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);" } l10n/es.json 0000604 00000056112 15247130447 0006630 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "¡No se puede escribir en el directorio \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Esto puede solucionarse fácilmente dándole al servidor permisos de escritura del directorio de configuración", "See %s" : "Mirar %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Habitualmente, esto puede arreglarse dando al servidor web acceso de escritura al directorio de configuración. Véase %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Los archivos de la aplicación %$1s no fueron reemplazados correctamente. Asegúrese que es una versión compatible con el servidor.", "Sample configuration detected" : "Ejemplo de configuración detectado", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Se ha detectado que el ejemplo de configuración ha sido copiado. Esto puede arruinar su instalación y es un caso para el que no se brinda soporte. Lea la documentación antes de hacer cambios en config.php", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s, y %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s y %5$s", "Education Edition" : "Edición Educación", "Enterprise bundle" : "Conjunto para empresas", "Groupware bundle" : "Conjunto de groupware", "Social sharing bundle" : "Conjunto para compartir en redes", "PHP %s or higher is required." : "Se requiere PHP %s o superior.", "PHP with a version lower than %s is required." : "PHP con una versión inferior que %s la requerida.", "%sbit or higher PHP required." : "Se requiere PHP %sbit o superior.", "Following databases are supported: %s" : "Las siguientes bases de datos están soportadas: %s", "The command line tool %s could not be found" : "No se encontró la herramienta %s de línea de comandos", "The library %s is not available." : "La biblioteca %s no está disponible", "Library %s with a version higher than %s is required - available version %s." : "Biblioteca %s con una versión superior que %s la requerida - versión disponible %s.", "Library %s with a version lower than %s is required - available version %s." : "Biblioteca %s con una versión inferior que %s la requerida - versión disponible %s.", "Following platforms are supported: %s" : "Las siguientes plataformas están soportadas: %s", "Server version %s or higher is required." : "Se necesita la versión %s o superior del servidor.", "Server version %s or lower is required." : "Se necesita la versión %s o inferior del servidor. ", "Unknown filetype" : "Tipo de archivo desconocido", "Invalid image" : "Imagen inválida", "Avatar image is not square" : "La imagen de avatar no es cuadrada", "today" : "hoy", "yesterday" : "ayer", "_%n day ago_::_%n days ago_" : ["Hace %n día","hace %n días"], "last month" : "mes pasado", "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"], "last year" : "año pasado", "_%n year ago_::_%n years ago_" : ["Hace %n año","hace %n años"], "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"], "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"], "seconds ago" : "hace segundos", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID %s no existe. Por favor, actívalo en la configuración de apps o contacta con tu administrador.", "File name is a reserved word" : "El nombre de archivo es una palabra reservada", "File name contains at least one invalid character" : "El nombre del archivo contiene al menos un carácter inválido", "File name is too long" : "El nombre del archivo es demasiado largo", "Dot files are not allowed" : "Los archivos Dot no están permitidos", "Empty filename is not allowed" : "No se puede dejar el nombre en blanco.", "App \"%s\" cannot be installed because appinfo file cannot be read." : "La app \"%s\" no puede ser instalada debido a que no se puede leer la información de la app.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "La aplicación \"%s\" no se puede instalar porque no es compatible con esta versión del servidor.", "This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, por favor no responda.", "Help" : "Ayuda", "Apps" : "Aplicaciones", "Settings" : "Configuración", "Log out" : "Desconectar", "Users" : "Usuarios", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Ajustes Basicas", "Sharing" : "Compartir", "Security" : "Seguridad", "Encryption" : "Cifrado", "Additional settings" : "Configuración adicional", "Tips & tricks" : "Sugerencias y trucos", "Personal info" : "Información personal", "Sync clients" : "Clientes de sincronización", "Unlimited" : "Ilimitado", "__language_name__" : "Español", "Verifying" : "Verificando", "Verifying …" : "Verificando...", "Verify" : "Verificar", "%s enter the database username and name." : "%s introduzca el nombre de usuario y la contraseña de la BBDD.", "%s enter the database username." : "%s ingresar el usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s puede utilizar puntos en el nombre de la base de datos", "Oracle connection could not be established" : "No se pudo establecer la conexión a Oracle", "Oracle username and/or password not valid" : "Usuario y/o contraseña de Oracle no válidos", "PostgreSQL username and/or password not valid" : "Usuario y/o contraseña de PostgreSQL no válidos", "You need to enter details of an existing account." : "Necesita ingresar detalles de una cuenta existente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X no está soportado y %s no funcionará bien en esta plataforma. ¡Úsela bajo su propio riesgo! ", "For the best results, please consider using a GNU/Linux server instead." : "Para resultados óptimos, considere utilizar un servidor GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Parece que esta instancia %s está funcionando en un entorno PHP de 32-bits y el open_basedir se ha configurado en php.ini. Esto acarreará problemas con arhivos de tamaño superior a 4GB y resulta totalmente desaconsejado.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor, quite el ajuste de open_basedir —dentro de su php.ini— o pásese a PHP de 64 bits.", "Set an admin username." : "Configurar un nombre de usuario del administrador", "Set an admin password." : "Configurar la contraseña del administrador.", "Can't create or write into the data directory %s" : "No es posible crear o escribir en el directorio de datos %s", "Invalid Federated Cloud ID" : "ID Nube federada inválida", "Sharing %s failed, because the backend does not allow shares from type %i" : "No se pudo compartir %s porque el repositorio no permite recursos compartidos del tipo %i", "Sharing %s failed, because the file does not exist" : "No se pudo compartir %s porque el archivo no existe", "You are not allowed to share %s" : "Usted no está autorizado para compartir %s", "Sharing %s failed, because you can not share with yourself" : "Se falló al compartir %s, porque no puedes compartir contigo mismo", "Sharing %s failed, because the user %s does not exist" : "Se ha fallado al compartir %s, ya que el usuario %s no existe", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Se ha fallado al compartir %s, ya que el usuario %s no es miembro de ningún grupo del que %s sea miembro", "Sharing %s failed, because this item is already shared with %s" : "Se falló al compartir %s, ya que este elemento ya está compartido con %s", "Sharing %s failed, because this item is already shared with user %s" : "Compartiendo %s falló, porque este objeto ya se comparte con el usuario %s", "Sharing %s failed, because the group %s does not exist" : "Se falló al compartir %s, ya que el grupo %s no existe", "Sharing %s failed, because %s is not a member of the group %s" : "Se falló al compartir %s, ya que %s no es miembro del grupo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Es necesario definir una contraseña para crear un enlace publico. Solo los enlaces protegidos están permitidos", "Sharing %s failed, because sharing with links is not allowed" : "Se falló al compartir %s, ya que no está permitida la compartición con enlaces", "Not allowed to create a federated share with the same user" : "No se permite crear un recurso compartido federado con el mismo usuario", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Se falló al compartir %s. No se pudo hallar %s, quizás haya un problema de conexión con el servidor.", "Share type %s is not valid for %s" : "Compartir tipo %s no es válido para %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "No se puede fijar fecha de caducidad. Los archivos compartidos no pueden caducar más tarde de %s de ser compartidos", "Cannot set expiration date. Expiration date is in the past" : "No se puede fijar la fecha de caducidad. La fecha de caducidad está en el pasado.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El motor compartido %s debe implementar la interfaz OCP\\Share_Backend", "Sharing backend %s not found" : "El motor compartido %s no se ha encontrado", "Sharing backend for %s not found" : "Motor compartido para %s no encontrado", "Sharing failed, because the user %s is the original sharer" : "Se ha fallado al compartir, ya que el usuario %s es el compartidor original", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Se ha fallado al compartir %s, ya que los permisos superan los permisos otorgados a %s", "Sharing %s failed, because resharing is not allowed" : "Fallo al compartir %s, ya que no está permitido volverlo a compartir", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Se ha fallado al compartir %s porque el motor compartido para %s podría no encontrar su origen", "Sharing %s failed, because the file could not be found in the file cache" : "Se ha fallado al compartir %s, ya que el archivo no pudo ser encontrado en el cache de archivo", "Can’t increase permissions of %s" : "No se pueden aumentar los permisos de %s", "Files can’t be shared with delete permissions" : "Los archivos no se pueden compartir con permisos de borrado", "Files can’t be shared with create permissions" : "Los archivos no se pueden compartir con permisos de creación", "Expiration date is in the past" : "Ha pasado la fecha de caducidad", "Can’t set expiration date more than %s days in the future" : "No se puede establecer la fecha de expiración a más de %s días en el futuro", "%s shared »%s« with you" : "%s ha compartido »%s« contigo", "%s shared »%s« with you." : "%s ha compartido »%s« contigo", "Click the button below to open it." : "Haz clic en el botón de abajo para abrirlo.", "Open »%s«" : "Abrir »%s« ", "%s via %s" : "%s vía %s", "The requested share does not exist anymore" : "El recurso compartido solicitado ya no existe", "Could not find category \"%s\"" : "No puede encontrar la categoría \"%s\"", "Sunday" : "Domingo", "Monday" : "Lunes", "Tuesday" : "Martes", "Wednesday" : "Miércoles", "Thursday" : "Jueves", "Friday" : "Viernes", "Saturday" : "Sábado", "Sun." : "Dom.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mié.", "Thu." : "Jue.", "Fri." : "Vie.", "Sat." : "Sáb.", "Su" : "Do", "Mo" : "Lu", "Tu" : "Ma", "We" : "Mi", "Th" : "Ju", "Fr" : "Vi", "Sa" : "Sa", "January" : "Enero", "February" : "Febrero", "March" : "Marzo", "April" : "Abril", "May" : "Mayo", "June" : "Junio", "July" : "Julio", "August" : "Agosto", "September" : "Septiembre", "October" : "Octubre", "November" : "Noviembre", "December" : "Diciembre", "Jan." : "Ene.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "May.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Ago.", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Dic.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Solo los siguientes caracteres están permitidos en un nombre de usuario: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Se debe proporcionar un nombre de usuario válido", "Username contains whitespace at the beginning or at the end" : "El nombre de usuario contiene espacios en blanco al principio o al final", "Username must not consist of dots only" : "El nombre de usuario no debe consistir solo de puntos", "A valid password must be provided" : "Se debe proporcionar una contraseña válida", "The username is already being used" : "El nombre de usuario ya está en uso", "Could not create user" : "No se ha podido crear el usuario", "User disabled" : "Usuario deshabilitado", "Login canceled by app" : "Login cancelado por la app", "No app name specified" : "No se ha especificado nombre de la aplicación", "App '%s' could not be installed!" : "¡No se pudo instalar la app '%s'!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "La app \"%s\" no puede instalarse porque las siguientes dependencias no están cumplimentadas: %s", "a safe home for all your data" : "un hogar seguro para todos tus datos", "File is currently busy, please try again later" : "Archivo se encuentra actualmente ocupado, por favor inténtelo de nuevo más tarde", "Can't read file" : "No se puede leer archivo", "Application is not enabled" : "La aplicación no está habilitada", "Authentication error" : "Error de autenticación", "Token expired. Please reload page." : "Token expirado. Por favor, recarge la página.", "Unknown user" : "Usuario desconocido", "No database drivers (sqlite, mysql, or postgresql) installed." : "No están instalados los drivers de BBDD (sqlite, mysql, o postgresql)", "Cannot write into \"config\" directory" : "No se puede escribir el el directorio de configuración", "Cannot write into \"apps\" directory" : "No se puede escribir en el directorio de \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Habitualmente, esto puede arreglarse dando al servidor web acceso de escritura al directorio de apps o desactivando la tienda de apps en el archivo de configuración. Véase %s", "Cannot create \"data\" directory" : "No es posible crear el directorio \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Habitualmente, esto puede arreglarse dando al servidor web acceso de escritura al directorio raíz. Véase %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Habitualmente, los permisos pueden arreglarse dando al servidor web acceso de escritura al directorio raíz. Véase %s", "Setting locale to %s failed" : "Falló la activación del idioma %s ", "Please install one of these locales on your system and restart your webserver." : "Instale uno de estos idiomas en su sistema y reinicie su servidor web.", "Please ask your server administrator to install the module." : "Consulte al administrador de su servidor para instalar el módulo.", "PHP module %s not installed." : "El módulo PHP %s no está instalado.", "PHP setting \"%s\" is not set to \"%s\"." : "La opción PHP \"%s\" no es \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ajustar esta configuración en php.ini hará que Nextcloud funcione de nuevo", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload está dispuesta en \"%s\" en lugar del valor esperado \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Para solucionarlo, defina la función <code>mbstring.func_overload</code> a <code>0</code> en su php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 es requerido en esta o en versiones superiores. Ahora mismo tienes instalada %s.", "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este error, actualiza la versión de tu libxml2 y reinicia el servidor web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP está aparentemente configurado para eliminar bloques de documentos en línea. Esto hará que varias aplicaciones principales estén inaccesibles.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Probablemente esto venga a causa de la caché o un acelerador, tales como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Los módulos PHP se han instalado, pero aparecen listados como si faltaran", "Please ask your server administrator to restart the web server." : "Consulte al administrador de su servidor para reiniciar el servidor web.", "PostgreSQL >= 9 required" : "PostgreSQL 9 o superior requerido.", "Please upgrade your database version" : "Actualice su versión de base de datos.", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor cambie los permisos a 0770 para que el directorio no se pueda mostrar para otros usuarios.", "Your data directory is readable by other users" : "Su directorio data es leible por otros usuarios", "Your data directory must be an absolute path" : "Su directorio data debe ser una ruta absoluta", "Check the value of \"datadirectory\" in your configuration" : "Compruebe el valor de \"datadirectory\" en su configuración.", "Your data directory is invalid" : "Su directorio de datos es inválido", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Asegúrate de que existe un archivo llamado \".ocdata\" en la raíz del directorio de datos.", "Could not obtain lock type %d on \"%s\"." : "No se pudo realizar el bloqueo %d en \"%s\".", "Storage unauthorized. %s" : "Almacenamiento no autorizado. %s", "Storage incomplete configuration. %s" : "Configuración de almacenamiento incompleta. %s", "Storage connection error. %s" : "Error de conexión de almacenamiento. %s", "Storage is temporarily not available" : "El almacenamiento no esta disponible temporalmente", "Storage connection timeout. %s" : "Tiempo de conexión de almacenamiento agotado. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Esto puede solucionarse fácilmente %sotorgándole permisos de escritura al directorio de configuración%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Módulo con id: %s no existe. Por favor habilítelo en los ajustes de sus aplicaciones o contáctese con su administrador.", "Server settings" : "Configuración del servidor", "DB Error: \"%s\"" : "Error BD: \"%s\"", "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando infractor: \"%s\", nombre: %s, contraseña: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "La configuración de permisos para %s ha fallado, ya que los permisos superan los permisos dados a %s", "Setting permissions for %s failed, because the item was not found" : "La configuración de permisos para %s ha fallado, ya que no se encontró el elemento ", "Cannot clear expiration date. Shares are required to have an expiration date." : "No se puede eliminar la fecha de caducidad. Los archivos compartidos deben tener una fecha de caducidad.", "Cannot increase permissions of %s" : "No se pueden incrementar los permisos de %s", "Files can't be shared with delete permissions" : "Los archivos no pueden ser compartidos con permisos de borrado", "Files can't be shared with create permissions" : "Los arhivos no pueden ser compartidos con permisos de creación", "Cannot set expiration date more than %s days in the future" : "No se puede fijar la fecha de caducidad más de %s días en el futuro.", "Personal" : "Personal", "Admin" : "Administración", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Esto puede solucionarse fácilmente %sdándole permisos de escritura al servidor en el directorio%s de apps o deshabilitando la tienda de apps en el archivo de configuración.", "Cannot create \"data\" directory (%s)" : "No puedo crear del directorio \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Normalmente esto se puede solucionar <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">dándole al servidor web permisos de escritura en todo el directorio o el directorio 'root'</a>", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Los permisos normalmente puede solucionarse %sdándole al servidor permisos de escritura del directorio raíz%s.", "Data directory (%s) is readable by other users" : "El directorio de datos (%s) se puede leer por otros usuarios.", "Data directory (%s) must be an absolute path" : "El directorio de datos (%s) debe ser una ruta absoluta", "Data directory (%s) is invalid" : "El directorio de datos (%s) no es válido", "Please check that the data directory contains a file \".ocdata\" in its root." : "Verifique que el directorio de datos contiene un archivo \".ocdata\" en su directorio raíz." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/fr.json 0000604 00000057426 15247130447 0006641 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Impossible d’écrire dans le répertoire « config » !", "This can usually be fixed by giving the webserver write access to the config directory" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire \"config\"", "See %s" : "Voir %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire \"config\". Voir %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Les fichiers de l'application %$1s n'ont pas été remplacés correctement. Veuillez vérifier que c'est une version compatible avec le serveur.", "Sample configuration detected" : "Configuration d'exemple détectée", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Il a été détecté que la configuration donnée à titre d'exemple a été copiée. Cela peut rendre votre installation inopérante et n'est pas pris en charge. Veuillez lire la documentation avant d'effectuer des modifications dans config.php", "%1$s and %2$s" : "%1$s et %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s et %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s et %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s et %5$s", "Education Edition" : "Édition pour l'éducation ", "Enterprise bundle" : "Pack pour entreprise", "Groupware bundle" : "Pack pour travail collaboratif", "Social sharing bundle" : "Pack pour partage social", "PHP %s or higher is required." : "PHP %s ou supérieur est requis.", "PHP with a version lower than %s is required." : "PHP avec une version antérieure à %s est requis.", "%sbit or higher PHP required." : "PHP %sbits ou supérieur est requis.", "Following databases are supported: %s" : "Les bases de données suivantes sont supportées : %s", "The command line tool %s could not be found" : "La commande %s est introuvable", "The library %s is not available." : "La librairie %s n'est pas disponible.", "Library %s with a version higher than %s is required - available version %s." : "La librairie %s doit être au moins à la version %s. Version disponible : %s.", "Library %s with a version lower than %s is required - available version %s." : "La librairie %s doit avoir une version antérieure à %s. Version disponible : %s.", "Following platforms are supported: %s" : "Les plateformes suivantes sont prises en charge : %s", "Server version %s or higher is required." : "Un serveur de version %s ou supérieure est requis.", "Server version %s or lower is required." : "Un serveur de version %s ou inférieure est requis.", "Unknown filetype" : "Type de fichier inconnu", "Invalid image" : "Image non valable", "Avatar image is not square" : "L'image d'avatar n'est pas carré", "today" : "aujourd'hui", "yesterday" : "hier", "_%n day ago_::_%n days ago_" : ["il y a %n jour","il y a %n jours"], "last month" : "le mois dernier", "_%n month ago_::_%n months ago_" : ["Il y a %n mois","Il y a %n mois"], "last year" : "l'année dernière", "_%n year ago_::_%n years ago_" : ["il y a %n an","il y a %n ans"], "_%n hour ago_::_%n hours ago_" : ["Il y a %n heure","Il y a %n heures"], "_%n minute ago_::_%n minutes ago_" : ["il y a %n minute","il y a %n minutes"], "seconds ago" : "il y a quelques secondes", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Le module avec l'ID: %s n'existe pas. Merci de l'activer dans les paramètres d'applications ou de contacter votre administrateur.", "File name is a reserved word" : "Ce nom de fichier est un mot réservé", "File name contains at least one invalid character" : "Le nom de fichier contient un (des) caractère(s) non valide(s)", "File name is too long" : "Nom de fichier trop long", "Dot files are not allowed" : "Le nom de fichier ne peut pas commencer par un point", "Empty filename is not allowed" : "Le nom de fichier ne peut pas être vide", "App \"%s\" cannot be installed because appinfo file cannot be read." : "L'application \"%s\" ne peut pas être installée car le fichier appinfo ne peut pas être lu.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "L'application \"%s\" ne peut être installée car elle n'est pas compatible avec cette version du serveur", "This is an automatically sent email, please do not reply." : "Ceci est un e-mail envoyé automatiquement, veuillez ne pas y répondre.", "Help" : "Aide", "Apps" : "Applications", "Settings" : "Paramètres", "Log out" : "Se déconnecter", "Users" : "Utilisateurs", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Paramètres de base", "Sharing" : "Partage", "Security" : "Sécurité", "Encryption" : "Chiffrement", "Additional settings" : "Paramètres supplémentaires", "Tips & tricks" : "Trucs et astuces", "Personal info" : "Informations personnelles", "Sync clients" : "Clients de synchronisation", "Unlimited" : "Illimité", "__language_name__" : "Français", "Verifying" : "Vérification en cours", "Verifying …" : "Vérification en cours...", "Verify" : "Vérifié", "%s enter the database username and name." : "%s entrez le nom d'utilisateur et le nom de la base de données.", "%s enter the database username." : "%s entrez le nom d'utilisateur de la base de données.", "%s enter the database name." : "%s entrez le nom de la base de données.", "%s you may not use dots in the database name" : "%s vous ne pouvez pas utiliser de points dans le nom de la base de données", "Oracle connection could not be established" : "La connexion Oracle ne peut être établie", "Oracle username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base Oracle non valide(s)", "PostgreSQL username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base PostgreSQL non valide(s)", "You need to enter details of an existing account." : "Vous devez indiquer les détails d'un compte existant.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X n'est pas pris en charge et %s ne fonctionnera pas correctement sur cette plate-forme. Son utilisation est à vos risques et périls !", "For the best results, please consider using a GNU/Linux server instead." : "Pour obtenir les meilleurs résultats, vous devriez utiliser un serveur GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Il semble que cette instance %s fonctionne sur un environnement PHP 32-bit et open_basedir a été configuré dans php.ini. Cela engendre des problèmes avec les fichiers de taille supérieure à 4 Go et est donc fortement déconseillé.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Veuillez supprimer la configuration open_basedir de votre php.ini ou utiliser une version PHP 64-bit.", "Set an admin username." : "Spécifiez un nom d'utilisateur pour l'administrateur.", "Set an admin password." : "Spécifiez un mot de passe pour l'administrateur.", "Can't create or write into the data directory %s" : "Impossible de créer, ou d'écrire dans, le répertoire des données %s", "Invalid Federated Cloud ID" : "ID Federated Cloud incorrect", "Sharing %s failed, because the backend does not allow shares from type %i" : "Le partage de %s a échoué car l’infrastructure n'autorise pas les partages de type %i", "Sharing %s failed, because the file does not exist" : "Le partage de %s a échoué car le fichier n'existe pas", "You are not allowed to share %s" : "Vous n'êtes pas autorisé à partager %s", "Sharing %s failed, because you can not share with yourself" : "Le partage de %s a échoué car vous ne pouvez pas partager avec vous-même", "Sharing %s failed, because the user %s does not exist" : "Le partage de %s a échoué car l'utilisateur %s n'existe pas", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Le partage de %s a échoué car l'utilisateur %s n'est membre d'aucun groupe auquel %s appartient", "Sharing %s failed, because this item is already shared with %s" : "Le partage de %s a échoué car cet objet est déjà partagé avec %s", "Sharing %s failed, because this item is already shared with user %s" : "Le partage de %s a échoué car cet élément est déjà partagé avec l'utilisateur %s", "Sharing %s failed, because the group %s does not exist" : "Le partage de %s a échoué car le groupe %s n'existe pas", "Sharing %s failed, because %s is not a member of the group %s" : "Le partage de %s a échoué car %s n'est pas membre du groupe %s", "You need to provide a password to create a public link, only protected links are allowed" : "Vous devez fournir un mot de passe pour créer un lien public, seuls les liens protégés sont autorisées.", "Sharing %s failed, because sharing with links is not allowed" : "Le partage de %s a échoué car le partage par lien n'est pas permis", "Not allowed to create a federated share with the same user" : "Non autorisé à créer un partage fédéré avec le même utilisateur", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Le partage de %s a échoué : impossible de trouver %s. Peut-être le serveur est-il momentanément injoignable.", "Share type %s is not valid for %s" : "Le type de partage %s n'est pas valide pour %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Impossible de configurer la date d'expiration. Un partage ne peut expirer plus de %s après sa création", "Cannot set expiration date. Expiration date is in the past" : "Impossible de configurer la date d'expiration : elle est dans le passé.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Le service de partage %s doit implémenter l'interface OCP\\Share_Backend", "Sharing backend %s not found" : "Service de partage %s non trouvé", "Sharing backend for %s not found" : "Le service de partage pour %s est introuvable", "Sharing failed, because the user %s is the original sharer" : "Le partage a échoué car l'utilisateur %s est le propriétaire original", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Le partage de %s a échoué car les permissions dépassent celles accordées à %s", "Sharing %s failed, because resharing is not allowed" : "Le partage de %s a échoué car le repartage n'est pas autorisé", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Le partage de %s a échoué car le service %s n'a pas trouvé sa source..", "Sharing %s failed, because the file could not be found in the file cache" : "Le partage de %s a échoué car le fichier n'a pas été trouvé dans les fichiers mis en cache.", "Can’t increase permissions of %s" : "Impossible d'augmenter les permissions de %s", "Files can’t be shared with delete permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de suppression", "Files can’t be shared with create permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de création", "Expiration date is in the past" : "La date d'expiration est dans le passé", "Can’t set expiration date more than %s days in the future" : "Impossible de définir la date d'expiration à plus de %s jours dans le futur", "%s shared »%s« with you" : "%s a partagé «%s» avec vous", "%s shared »%s« with you." : "%s a partagé «%s» avec vous.", "Click the button below to open it." : "Cliquez sur le bouton ci-dessous pour l'ouvrir", "Open »%s«" : "Ouvrir «%s»", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Le partage demandé n'existe plus", "Could not find category \"%s\"" : "Impossible de trouver la catégorie \"%s\"", "Sunday" : "Dimanche", "Monday" : "Lundi", "Tuesday" : "Mardi", "Wednesday" : "Mercredi", "Thursday" : "Jeudi", "Friday" : "Vendredi", "Saturday" : "Samedi", "Sun." : "Dim.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mer.", "Thu." : "Jeu.", "Fri." : "Ven.", "Sat." : "Sam.", "Su" : "Di", "Mo" : "Lu", "Tu" : "Ma", "We" : "Me", "Th" : "Je", "Fr" : "Ve", "Sa" : "Sa", "January" : "Janvier", "February" : "Février", "March" : "Mars", "April" : "Avril", "May" : "Mai", "June" : "Juin", "July" : "Juillet", "August" : "Août", "September" : "Septembre", "October" : "Octobre", "November" : "Novembre", "December" : "Décembre", "Jan." : "Jan.", "Feb." : "Fév.", "Mar." : "Mars", "Apr." : "Avr.", "May." : "Mai", "Jun." : "Juin", "Jul." : "Juil.", "Aug." : "Août", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Déc.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Seuls les caractères suivants sont autorisés dans un nom d'utilisateur : \"a-z\", \"A-Z\", \"0-9\", \"_@-\" et \".\" (le point)", "A valid username must be provided" : "Un nom d'utilisateur valide doit être saisi", "Username contains whitespace at the beginning or at the end" : "Le nom d'utilisateur contient des espaces au début ou à la fin", "Username must not consist of dots only" : "Le nom d'utilisateur ne doit pas être composé uniquement de points", "A valid password must be provided" : "Un mot de passe valide doit être saisi", "The username is already being used" : "Ce nom d'utilisateur est déjà utilisé", "Could not create user" : "Impossible de créer l'utilisateur", "User disabled" : "Utilisateur désactivé", "Login canceled by app" : "L'authentification a été annulé par l'application", "No app name specified" : "Aucun nom d'application spécifié", "App '%s' could not be installed!" : "L'application \"%s\" ne peut pas être installée !", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "L'application \"%s\" ne peut pas être installée à cause des dépendances suivantes non satisfaites : %s", "a safe home for all your data" : "un endroit sûr pour toutes vos données", "File is currently busy, please try again later" : "Le fichier est actuellement utilisé, veuillez réessayer plus tard", "Can't read file" : "Impossible de lire le fichier", "Application is not enabled" : "L'application n'est pas activée", "Authentication error" : "Erreur d'authentification", "Token expired. Please reload page." : "La session a expiré. Veuillez recharger la page.", "Unknown user" : "Utilisateur inconnu", "No database drivers (sqlite, mysql, or postgresql) installed." : "Aucun pilote de base de données n’est installé (sqlite, mysql ou postgresql).", "Cannot write into \"config\" directory" : "Impossible d’écrire dans le répertoire \"config\"", "Cannot write into \"apps\" directory" : "Impossible d’écrire dans le répertoire \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire \"apps\" ou en désactivant l'appstore dans le fichier de configuration. Voir %s", "Cannot create \"data\" directory" : "Impossible de créer le dossier \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire racine. Voir %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Le problème de permissions peut généralement être résolu en donnant au serveur web un accès en écriture au répertoire racine. Voir %s.", "Setting locale to %s failed" : "Echec de la spécification des paramètres régionaux à %s", "Please install one of these locales on your system and restart your webserver." : "Veuillez installer l'un de ces paramètres régionaux sur votre système et redémarrer votre serveur web.", "Please ask your server administrator to install the module." : "Veuillez demander à votre administrateur d’installer le module.", "PHP module %s not installed." : "Le module PHP %s n’est pas installé.", "PHP setting \"%s\" is not set to \"%s\"." : "Le paramètre PHP \"%s\" n'est pas \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ajuster ce paramètre dans php.ini fera fonctionner Nextcould à nouveau", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload est à \"%s\" alors que la valeur \"0\" est attendue", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Pour corriger ce problème mettez <code>mbstring.func_overload</code> à <code>0</code> dans votre php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 au moins est requis. Actuellement %s est installé.", "To fix this issue update your libxml2 version and restart your web server." : "Pour régler ce problème, mettez à jour votre version de libxml2 et redémarrez votre serveur web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP semble configuré de manière à supprimer les blocs PHPdoc du code. Cela rendra plusieurs applications de base inaccessibles.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "La raison est probablement l'utilisation d'un cache / accélérateur tel que Zend OPcache ou eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Les modules PHP ont été installés mais sont toujours indiqués comme manquants ?", "Please ask your server administrator to restart the web server." : "Veuillez demander à votre administrateur serveur de redémarrer le serveur web.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 requis", "Please upgrade your database version" : "Veuillez mettre à jour votre gestionnaire de base de données", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Veuillez changer les permissions du répertoire en mode 0770 afin que son contenu ne puisse pas être listé par les autres utilisateurs.", "Your data directory is readable by other users" : "Votre répertoire est lisible par les autres utilisateurs", "Your data directory must be an absolute path" : "Le chemin de votre répertoire doit être un lien absolu", "Check the value of \"datadirectory\" in your configuration" : "Verifiez la valeur de \"datadirectory\" dans votre configuration", "Your data directory is invalid" : "Votre répertoire n'est pas valide", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Assurez-vous que le répertoire de données contient un fichier \".ocdata\" à sa racine.", "Could not obtain lock type %d on \"%s\"." : "Impossible d'obtenir le verrouillage de type %d sur \"%s\".", "Storage unauthorized. %s" : "Espace de stockage non autorisé. %s", "Storage incomplete configuration. %s" : "Configuration de l'espace de stockage incomplète. %s", "Storage connection error. %s" : "Erreur de connexion à l'espace stockage. %s", "Storage is temporarily not available" : "Le support de stockage est temporairement indisponible", "Storage connection timeout. %s" : "Le délai d'attente pour la connexion à l'espace de stockage a été dépassé. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Ce problème est généralement résolu %sen donnant au serveur web un accès en écriture au répertoire de configuration%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Le module avec l'id: %s n'existe pas. Merci de l'activer dans les paramètres d'applications ou de contacter votre administrateur.", "Server settings" : "Paramètres serveur", "DB Error: \"%s\"" : "Erreur de la base de données : \"%s\"", "Offending command was: \"%s\"" : "La requête en cause est : \"%s\"", "You need to enter either an existing account or the administrator." : "Vous devez indiquer un compte existant ou celui de l'administrateur.", "Offending command was: \"%s\", name: %s, password: %s" : "La requête en cause est : \"%s\", nom : %s, mot de passe : %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Le réglage des permissions pour %s a échoué car les permissions dépassent celles accordées à %s", "Setting permissions for %s failed, because the item was not found" : "Le réglage des permissions pour %s a échoué car l'objet n'a pas été trouvé", "Cannot clear expiration date. Shares are required to have an expiration date." : "Impossible de supprimer la date d'expiration. Les partages doivent avoir une date d'expiration.", "Cannot increase permissions of %s" : "Impossible d'augmenter les permissions de %s", "Files can't be shared with delete permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de suppression", "Files can't be shared with create permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de création", "Cannot set expiration date more than %s days in the future" : "Impossible de définir la date d'expiration à plus de %s jours dans le futur", "Personal" : "Personnel", "Admin" : "Administration", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Ce problème est généralement résolu %sen donnant au serveur web un accès en écriture au répertoire apps%s ou en désactivant l'appstore dans le fichier de configuration.", "Cannot create \"data\" directory (%s)" : "Impossible de créer le répertoire \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Ce problème est généralement résolu <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">en donnant au serveur web un accès en écriture au répertoire racine</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Le problème de permissions peut généralement être résolu %sen donnant au serveur web un accès en écriture au répertoire racine%s", "Data directory (%s) is readable by other users" : "Le répertoire de données (%s) est lisible par les autres utilisateurs", "Data directory (%s) must be an absolute path" : "Le chemin du dossier de données (%s) doit être absolu", "Data directory (%s) is invalid" : "Le répertoire (%s) n'est pas valide", "Please check that the data directory contains a file \".ocdata\" in its root." : "Veuillez vérifier que le répertoire de données contient un fichier \".ocdata\" à sa racine." },"pluralForm" :"nplurals=2; plural=(n > 1);" } l10n/en_GB.json 0000604 00000052210 15247130447 0007166 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "Cannot write into \"config\" directory!", "This can usually be fixed by giving the webserver write access to the config directory" : "This can usually be fixed by giving the webserver write access to the config directory", "See %s" : "See %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "This can usually be fixed by giving the webserver write access to the config directory. See %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.", "Sample configuration detected" : "Sample configuration detected", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php", "%1$s and %2$s" : "%1$s and %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s and %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s and %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s and %5$s", "Education Edition" : "Education Edition", "Enterprise bundle" : "Enterprise bundle", "Groupware bundle" : "Groupware bundle", "Social sharing bundle" : "Social sharing bundle", "PHP %s or higher is required." : "PHP %s or higher is required.", "PHP with a version lower than %s is required." : "PHP with a version lower than %s is required.", "%sbit or higher PHP required." : "%sbit or higher PHP required.", "Following databases are supported: %s" : "Following databases are supported: %s", "The command line tool %s could not be found" : "The command line tool %s could not be found", "The library %s is not available." : "The library %s is not available.", "Library %s with a version higher than %s is required - available version %s." : "Library %s with a version higher than %s is required - available version %s.", "Library %s with a version lower than %s is required - available version %s." : "Library %s with a version lower than %s is required - available version %s.", "Following platforms are supported: %s" : "Following platforms are supported: %s", "Server version %s or higher is required." : "Server version %s or higher is required.", "Server version %s or lower is required." : "Server version %s or lower is required.", "Unknown filetype" : "Unknown filetype", "Invalid image" : "Invalid image", "Avatar image is not square" : "Avatar image is not square", "today" : "today", "yesterday" : "yesterday", "_%n day ago_::_%n days ago_" : ["%n day ago","%n days ago"], "last month" : "last month", "_%n month ago_::_%n months ago_" : ["%n month ago","%n months ago"], "last year" : "last year", "_%n year ago_::_%n years ago_" : ["%n year ago","%n years ago"], "_%n hour ago_::_%n hours ago_" : ["%n hour ago","%n hours ago"], "_%n minute ago_::_%n minutes ago_" : ["%n minute ago","%n minutes ago"], "seconds ago" : "seconds ago", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator.", "File name is a reserved word" : "File name is a reserved word", "File name contains at least one invalid character" : "File name contains at least one invalid character", "File name is too long" : "File name is too long", "Dot files are not allowed" : "Dot files are not allowed", "Empty filename is not allowed" : "Empty filename is not allowed", "App \"%s\" cannot be installed because appinfo file cannot be read." : "App \"%s\" cannot be installed because appinfo file cannot be read.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "App \"%s\" cannot be installed. It is not compatible with this version of the server.", "This is an automatically sent email, please do not reply." : "This is an automatically sent email, please do not reply.", "Help" : "Help", "Apps" : "Apps", "Settings" : "Settings", "Log out" : "Log out", "Users" : "Users", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Basic settings", "Sharing" : "Sharing", "Security" : "Security", "Encryption" : "Encryption", "Additional settings" : "Additional settings", "Tips & tricks" : "Tips & tricks", "Personal info" : "Personal info", "Sync clients" : "Sync clients", "Unlimited" : "Unlimited", "__language_name__" : "__language_name__", "Verifying" : "Verifying", "Verifying …" : "Verifying …", "Verify" : "Verify", "%s enter the database username and name." : "%s enter the database username and name.", "%s enter the database username." : "%s enter the database username.", "%s enter the database name." : "%s enter the database name.", "%s you may not use dots in the database name" : "%s you may not use dots in the database name", "Oracle connection could not be established" : "Oracle connection could not be established", "Oracle username and/or password not valid" : "Oracle username and/or password not valid", "PostgreSQL username and/or password not valid" : "PostgreSQL username and/or password not valid", "You need to enter details of an existing account." : "You need to enter details of an existing account.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! ", "For the best results, please consider using a GNU/Linux server instead." : "For the best results, please consider using a GNU/Linux server instead.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir setting has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP.", "Set an admin username." : "Set an admin username.", "Set an admin password." : "Set an admin password.", "Can't create or write into the data directory %s" : "Can't create or write into the data directory %s", "Invalid Federated Cloud ID" : "Invalid Federated Cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Sharing %s failed, because the backend does not allow shares from type %i", "Sharing %s failed, because the file does not exist" : "Sharing %s failed, because the file does not exist", "You are not allowed to share %s" : "You are not allowed to share %s", "Sharing %s failed, because you can not share with yourself" : "Sharing %s failed, because you can not share with yourself", "Sharing %s failed, because the user %s does not exist" : "Sharing %s failed, because the user %s does not exist", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of", "Sharing %s failed, because this item is already shared with %s" : "Sharing %s failed, because this item is already shared with %s", "Sharing %s failed, because this item is already shared with user %s" : "Sharing %s failed, because this item is already shared with user %s", "Sharing %s failed, because the group %s does not exist" : "Sharing %s failed, because the group %s does not exist", "Sharing %s failed, because %s is not a member of the group %s" : "Sharing %s failed, because %s is not a member of the group %s", "You need to provide a password to create a public link, only protected links are allowed" : "You need to provide a password to create a public link, only protected links are allowed", "Sharing %s failed, because sharing with links is not allowed" : "Sharing %s failed, because sharing with links is not allowed", "Not allowed to create a federated share with the same user" : "Not allowed to create a federated share with the same user", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Sharing %s failed, could not find %s, maybe the server is currently unreachable.", "Share type %s is not valid for %s" : "Share type %s is not valid for %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Cannot set expiry date. Shares cannot expire later than %s after they have been shared", "Cannot set expiration date. Expiration date is in the past" : "Cannot set expiry date. Expiry date is in the past", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Sharing backend %s must implement the interface OCP\\Share_Backend", "Sharing backend %s not found" : "Sharing backend %s not found", "Sharing backend for %s not found" : "Sharing backend for %s not found", "Sharing failed, because the user %s is the original sharer" : "Sharing failed, because the user %s is the original sharer", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Sharing %s failed, because the permissions exceed permissions granted to %s", "Sharing %s failed, because resharing is not allowed" : "Sharing %s failed, because resharing is not allowed", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Sharing %s failed, because the sharing backend for %s could not find its source", "Sharing %s failed, because the file could not be found in the file cache" : "Sharing %s failed, because the file could not be found in the file cache", "Can’t increase permissions of %s" : "Can’t increase permissions of %s", "Files can’t be shared with delete permissions" : "Files can’t be shared with delete permissions", "Files can’t be shared with create permissions" : "Files can’t be shared with create permissions", "Expiration date is in the past" : "Expiration date is in the past", "Can’t set expiration date more than %s days in the future" : "Can’t set expiration date more than %s days in the future", "%s shared »%s« with you" : "%s shared \"%s\" with you", "%s shared »%s« with you." : "%s shared »%s« with you.", "Click the button below to open it." : "Click the button below to open it.", "Open »%s«" : "Open »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "The requested share does not exist anymore", "Could not find category \"%s\"" : "Could not find category \"%s\"", "Sunday" : "Sunday", "Monday" : "Monday", "Tuesday" : "Tuesday", "Wednesday" : "Wednesday", "Thursday" : "Thursday", "Friday" : "Friday", "Saturday" : "Saturday", "Sun." : "Sun.", "Mon." : "Mon.", "Tue." : "Tue.", "Wed." : "Wed.", "Thu." : "Thu.", "Fri." : "Fri.", "Sat." : "Sat.", "Su" : "Su", "Mo" : "Mo", "Tu" : "Tu", "We" : "We", "Th" : "Th", "Fr" : "Fr", "Sa" : "Sa", "January" : "January", "February" : "February", "March" : "March", "April" : "April", "May" : "May", "June" : "June", "July" : "July", "August" : "August", "September" : "September", "October" : "October", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "May.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "A valid username must be provided", "Username contains whitespace at the beginning or at the end" : "Username contains whitespace at the beginning or at the end", "Username must not consist of dots only" : "Username must not consist of dots only", "A valid password must be provided" : "A valid password must be provided", "The username is already being used" : "The username is already being used", "Could not create user" : "Could not create user", "User disabled" : "User disabled", "Login canceled by app" : "Login cancelled by app", "No app name specified" : "No app name specified", "App '%s' could not be installed!" : "App '%s' could not be installed!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s", "a safe home for all your data" : "a safe home for all your data", "File is currently busy, please try again later" : "File is currently busy, please try again later", "Can't read file" : "Can't read file", "Application is not enabled" : "Application is not enabled", "Authentication error" : "Authentication error", "Token expired. Please reload page." : "Token expired. Please reload page.", "Unknown user" : "Unknown user", "No database drivers (sqlite, mysql, or postgresql) installed." : "No database drivers (sqlite, mysql, or postgresql) installed.", "Cannot write into \"config\" directory" : "Cannot write into \"config\" directory", "Cannot write into \"apps\" directory" : "Cannot write into \"apps\" directory", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s", "Cannot create \"data\" directory" : "Cannot create \"data\" directory", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "This can usually be fixed by giving the webserver write access to the root directory. See %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s.", "Setting locale to %s failed" : "Setting locale to %s failed", "Please install one of these locales on your system and restart your webserver." : "Please install one of these locales on your system and restart your webserver.", "Please ask your server administrator to install the module." : "Please ask your server administrator to install the module.", "PHP module %s not installed." : "PHP module %s not installed.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP setting \"%s\" is not set to \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Adjusting this setting in php.ini will allow Nextcloud to run", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 is at least required. Currently %s is installed.", "To fix this issue update your libxml2 version and restart your web server." : "To fix this issue update your libxml2 version and restart your web server.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP modules have been installed, but they are still listed as missing?", "Please ask your server administrator to restart the web server." : "Please ask your server administrator to restart the web server.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 required", "Please upgrade your database version" : "Please upgrade your database version", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Please change the permissions to 0770 so that the directory cannot be listed by other users.", "Your data directory is readable by other users" : "Your data directory is readable by other users", "Your data directory must be an absolute path" : "Your data directory must be an absolute path", "Check the value of \"datadirectory\" in your configuration" : "Check the value of \"datadirectory\" in your configuration", "Your data directory is invalid" : "Your data directory is invalid", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Ensure there is a file called \".ocdata\" in the root of the data directory.", "Could not obtain lock type %d on \"%s\"." : "Could not obtain lock type %d on \"%s\".", "Storage unauthorized. %s" : "Storage unauthorised. %s", "Storage incomplete configuration. %s" : "Storage incomplete configuration. %s", "Storage connection error. %s" : "Storage connection error. %s", "Storage is temporarily not available" : "Storage is temporarily not available", "Storage connection timeout. %s" : "Storage connection timeout. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "This can usually be fixed by %sgiving the webserver write access to the config directory%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator.", "Server settings" : "Server settings", "DB Error: \"%s\"" : "DB Error: \"%s\"", "Offending command was: \"%s\"" : "Offending command was: \"%s\"", "You need to enter either an existing account or the administrator." : "You need to enter either an existing account or the administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Offending command was: \"%s\", name: %s, password: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Setting permissions for %s failed, because the permissions exceed permissions granted to %s", "Setting permissions for %s failed, because the item was not found" : "Setting permissions for %s failed, because the item was not found", "Cannot clear expiration date. Shares are required to have an expiration date." : "Cannot clear expiration date. Shares are required to have an expiration date.", "Cannot increase permissions of %s" : "Cannot increase permissions of %s", "Files can't be shared with delete permissions" : "Files can't be shared with delete permissions", "Files can't be shared with create permissions" : "Files can't be shared with create permissions", "Cannot set expiration date more than %s days in the future" : "Cannot set expiration date more than %s days in the future", "Personal" : "Personal", "Admin" : "Admin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file.", "Cannot create \"data\" directory (%s)" : "Cannot create \"data\" directory (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s.", "Data directory (%s) is readable by other users" : "Data directory (%s) is readable by other users", "Data directory (%s) must be an absolute path" : "Data directory (%s) must be an absolute path", "Data directory (%s) is invalid" : "Data directory (%s) is invalid", "Please check that the data directory contains a file \".ocdata\" in its root." : "Please check that the data directory contains a file \".ocdata\" in its root." },"pluralForm" :"nplurals=2; plural=(n != 1);" } l10n/es.js 0000604 00000056115 15247130447 0006276 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "¡No se puede escribir en el directorio \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "Esto puede solucionarse fácilmente dándole al servidor permisos de escritura del directorio de configuración", "See %s" : "Mirar %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Habitualmente, esto puede arreglarse dando al servidor web acceso de escritura al directorio de configuración. Véase %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Los archivos de la aplicación %$1s no fueron reemplazados correctamente. Asegúrese que es una versión compatible con el servidor.", "Sample configuration detected" : "Ejemplo de configuración detectado", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Se ha detectado que el ejemplo de configuración ha sido copiado. Esto puede arruinar su instalación y es un caso para el que no se brinda soporte. Lea la documentación antes de hacer cambios en config.php", "%1$s and %2$s" : "%1$s y %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s y %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s, y %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s y %5$s", "Education Edition" : "Edición Educación", "Enterprise bundle" : "Conjunto para empresas", "Groupware bundle" : "Conjunto de groupware", "Social sharing bundle" : "Conjunto para compartir en redes", "PHP %s or higher is required." : "Se requiere PHP %s o superior.", "PHP with a version lower than %s is required." : "PHP con una versión inferior que %s la requerida.", "%sbit or higher PHP required." : "Se requiere PHP %sbit o superior.", "Following databases are supported: %s" : "Las siguientes bases de datos están soportadas: %s", "The command line tool %s could not be found" : "No se encontró la herramienta %s de línea de comandos", "The library %s is not available." : "La biblioteca %s no está disponible", "Library %s with a version higher than %s is required - available version %s." : "Biblioteca %s con una versión superior que %s la requerida - versión disponible %s.", "Library %s with a version lower than %s is required - available version %s." : "Biblioteca %s con una versión inferior que %s la requerida - versión disponible %s.", "Following platforms are supported: %s" : "Las siguientes plataformas están soportadas: %s", "Server version %s or higher is required." : "Se necesita la versión %s o superior del servidor.", "Server version %s or lower is required." : "Se necesita la versión %s o inferior del servidor. ", "Unknown filetype" : "Tipo de archivo desconocido", "Invalid image" : "Imagen inválida", "Avatar image is not square" : "La imagen de avatar no es cuadrada", "today" : "hoy", "yesterday" : "ayer", "_%n day ago_::_%n days ago_" : ["Hace %n día","hace %n días"], "last month" : "mes pasado", "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"], "last year" : "año pasado", "_%n year ago_::_%n years ago_" : ["Hace %n año","hace %n años"], "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"], "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"], "seconds ago" : "hace segundos", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID %s no existe. Por favor, actívalo en la configuración de apps o contacta con tu administrador.", "File name is a reserved word" : "El nombre de archivo es una palabra reservada", "File name contains at least one invalid character" : "El nombre del archivo contiene al menos un carácter inválido", "File name is too long" : "El nombre del archivo es demasiado largo", "Dot files are not allowed" : "Los archivos Dot no están permitidos", "Empty filename is not allowed" : "No se puede dejar el nombre en blanco.", "App \"%s\" cannot be installed because appinfo file cannot be read." : "La app \"%s\" no puede ser instalada debido a que no se puede leer la información de la app.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "La aplicación \"%s\" no se puede instalar porque no es compatible con esta versión del servidor.", "This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, por favor no responda.", "Help" : "Ayuda", "Apps" : "Aplicaciones", "Settings" : "Configuración", "Log out" : "Desconectar", "Users" : "Usuarios", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Ajustes Basicas", "Sharing" : "Compartir", "Security" : "Seguridad", "Encryption" : "Cifrado", "Additional settings" : "Configuración adicional", "Tips & tricks" : "Sugerencias y trucos", "Personal info" : "Información personal", "Sync clients" : "Clientes de sincronización", "Unlimited" : "Ilimitado", "__language_name__" : "Español", "Verifying" : "Verificando", "Verifying …" : "Verificando...", "Verify" : "Verificar", "%s enter the database username and name." : "%s introduzca el nombre de usuario y la contraseña de la BBDD.", "%s enter the database username." : "%s ingresar el usuario de la base de datos.", "%s enter the database name." : "%s ingresar el nombre de la base de datos", "%s you may not use dots in the database name" : "%s puede utilizar puntos en el nombre de la base de datos", "Oracle connection could not be established" : "No se pudo establecer la conexión a Oracle", "Oracle username and/or password not valid" : "Usuario y/o contraseña de Oracle no válidos", "PostgreSQL username and/or password not valid" : "Usuario y/o contraseña de PostgreSQL no válidos", "You need to enter details of an existing account." : "Necesita ingresar detalles de una cuenta existente.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X no está soportado y %s no funcionará bien en esta plataforma. ¡Úsela bajo su propio riesgo! ", "For the best results, please consider using a GNU/Linux server instead." : "Para resultados óptimos, considere utilizar un servidor GNU/Linux.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Parece que esta instancia %s está funcionando en un entorno PHP de 32-bits y el open_basedir se ha configurado en php.ini. Esto acarreará problemas con arhivos de tamaño superior a 4GB y resulta totalmente desaconsejado.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor, quite el ajuste de open_basedir —dentro de su php.ini— o pásese a PHP de 64 bits.", "Set an admin username." : "Configurar un nombre de usuario del administrador", "Set an admin password." : "Configurar la contraseña del administrador.", "Can't create or write into the data directory %s" : "No es posible crear o escribir en el directorio de datos %s", "Invalid Federated Cloud ID" : "ID Nube federada inválida", "Sharing %s failed, because the backend does not allow shares from type %i" : "No se pudo compartir %s porque el repositorio no permite recursos compartidos del tipo %i", "Sharing %s failed, because the file does not exist" : "No se pudo compartir %s porque el archivo no existe", "You are not allowed to share %s" : "Usted no está autorizado para compartir %s", "Sharing %s failed, because you can not share with yourself" : "Se falló al compartir %s, porque no puedes compartir contigo mismo", "Sharing %s failed, because the user %s does not exist" : "Se ha fallado al compartir %s, ya que el usuario %s no existe", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Se ha fallado al compartir %s, ya que el usuario %s no es miembro de ningún grupo del que %s sea miembro", "Sharing %s failed, because this item is already shared with %s" : "Se falló al compartir %s, ya que este elemento ya está compartido con %s", "Sharing %s failed, because this item is already shared with user %s" : "Compartiendo %s falló, porque este objeto ya se comparte con el usuario %s", "Sharing %s failed, because the group %s does not exist" : "Se falló al compartir %s, ya que el grupo %s no existe", "Sharing %s failed, because %s is not a member of the group %s" : "Se falló al compartir %s, ya que %s no es miembro del grupo %s", "You need to provide a password to create a public link, only protected links are allowed" : "Es necesario definir una contraseña para crear un enlace publico. Solo los enlaces protegidos están permitidos", "Sharing %s failed, because sharing with links is not allowed" : "Se falló al compartir %s, ya que no está permitida la compartición con enlaces", "Not allowed to create a federated share with the same user" : "No se permite crear un recurso compartido federado con el mismo usuario", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Se falló al compartir %s. No se pudo hallar %s, quizás haya un problema de conexión con el servidor.", "Share type %s is not valid for %s" : "Compartir tipo %s no es válido para %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "No se puede fijar fecha de caducidad. Los archivos compartidos no pueden caducar más tarde de %s de ser compartidos", "Cannot set expiration date. Expiration date is in the past" : "No se puede fijar la fecha de caducidad. La fecha de caducidad está en el pasado.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "El motor compartido %s debe implementar la interfaz OCP\\Share_Backend", "Sharing backend %s not found" : "El motor compartido %s no se ha encontrado", "Sharing backend for %s not found" : "Motor compartido para %s no encontrado", "Sharing failed, because the user %s is the original sharer" : "Se ha fallado al compartir, ya que el usuario %s es el compartidor original", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Se ha fallado al compartir %s, ya que los permisos superan los permisos otorgados a %s", "Sharing %s failed, because resharing is not allowed" : "Fallo al compartir %s, ya que no está permitido volverlo a compartir", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Se ha fallado al compartir %s porque el motor compartido para %s podría no encontrar su origen", "Sharing %s failed, because the file could not be found in the file cache" : "Se ha fallado al compartir %s, ya que el archivo no pudo ser encontrado en el cache de archivo", "Can’t increase permissions of %s" : "No se pueden aumentar los permisos de %s", "Files can’t be shared with delete permissions" : "Los archivos no se pueden compartir con permisos de borrado", "Files can’t be shared with create permissions" : "Los archivos no se pueden compartir con permisos de creación", "Expiration date is in the past" : "Ha pasado la fecha de caducidad", "Can’t set expiration date more than %s days in the future" : "No se puede establecer la fecha de expiración a más de %s días en el futuro", "%s shared »%s« with you" : "%s ha compartido »%s« contigo", "%s shared »%s« with you." : "%s ha compartido »%s« contigo", "Click the button below to open it." : "Haz clic en el botón de abajo para abrirlo.", "Open »%s«" : "Abrir »%s« ", "%s via %s" : "%s vía %s", "The requested share does not exist anymore" : "El recurso compartido solicitado ya no existe", "Could not find category \"%s\"" : "No puede encontrar la categoría \"%s\"", "Sunday" : "Domingo", "Monday" : "Lunes", "Tuesday" : "Martes", "Wednesday" : "Miércoles", "Thursday" : "Jueves", "Friday" : "Viernes", "Saturday" : "Sábado", "Sun." : "Dom.", "Mon." : "Lun.", "Tue." : "Mar.", "Wed." : "Mié.", "Thu." : "Jue.", "Fri." : "Vie.", "Sat." : "Sáb.", "Su" : "Do", "Mo" : "Lu", "Tu" : "Ma", "We" : "Mi", "Th" : "Ju", "Fr" : "Vi", "Sa" : "Sa", "January" : "Enero", "February" : "Febrero", "March" : "Marzo", "April" : "Abril", "May" : "Mayo", "June" : "Junio", "July" : "Julio", "August" : "Agosto", "September" : "Septiembre", "October" : "Octubre", "November" : "Noviembre", "December" : "Diciembre", "Jan." : "Ene.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Abr.", "May." : "May.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Ago.", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Dic.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Solo los siguientes caracteres están permitidos en un nombre de usuario: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Se debe proporcionar un nombre de usuario válido", "Username contains whitespace at the beginning or at the end" : "El nombre de usuario contiene espacios en blanco al principio o al final", "Username must not consist of dots only" : "El nombre de usuario no debe consistir solo de puntos", "A valid password must be provided" : "Se debe proporcionar una contraseña válida", "The username is already being used" : "El nombre de usuario ya está en uso", "Could not create user" : "No se ha podido crear el usuario", "User disabled" : "Usuario deshabilitado", "Login canceled by app" : "Login cancelado por la app", "No app name specified" : "No se ha especificado nombre de la aplicación", "App '%s' could not be installed!" : "¡No se pudo instalar la app '%s'!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "La app \"%s\" no puede instalarse porque las siguientes dependencias no están cumplimentadas: %s", "a safe home for all your data" : "un hogar seguro para todos tus datos", "File is currently busy, please try again later" : "Archivo se encuentra actualmente ocupado, por favor inténtelo de nuevo más tarde", "Can't read file" : "No se puede leer archivo", "Application is not enabled" : "La aplicación no está habilitada", "Authentication error" : "Error de autenticación", "Token expired. Please reload page." : "Token expirado. Por favor, recarge la página.", "Unknown user" : "Usuario desconocido", "No database drivers (sqlite, mysql, or postgresql) installed." : "No están instalados los drivers de BBDD (sqlite, mysql, o postgresql)", "Cannot write into \"config\" directory" : "No se puede escribir el el directorio de configuración", "Cannot write into \"apps\" directory" : "No se puede escribir en el directorio de \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Habitualmente, esto puede arreglarse dando al servidor web acceso de escritura al directorio de apps o desactivando la tienda de apps en el archivo de configuración. Véase %s", "Cannot create \"data\" directory" : "No es posible crear el directorio \"data\"", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Habitualmente, esto puede arreglarse dando al servidor web acceso de escritura al directorio raíz. Véase %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Habitualmente, los permisos pueden arreglarse dando al servidor web acceso de escritura al directorio raíz. Véase %s", "Setting locale to %s failed" : "Falló la activación del idioma %s ", "Please install one of these locales on your system and restart your webserver." : "Instale uno de estos idiomas en su sistema y reinicie su servidor web.", "Please ask your server administrator to install the module." : "Consulte al administrador de su servidor para instalar el módulo.", "PHP module %s not installed." : "El módulo PHP %s no está instalado.", "PHP setting \"%s\" is not set to \"%s\"." : "La opción PHP \"%s\" no es \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ajustar esta configuración en php.ini hará que Nextcloud funcione de nuevo", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload está dispuesta en \"%s\" en lugar del valor esperado \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Para solucionarlo, defina la función <code>mbstring.func_overload</code> a <code>0</code> en su php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 es requerido en esta o en versiones superiores. Ahora mismo tienes instalada %s.", "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este error, actualiza la versión de tu libxml2 y reinicia el servidor web.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP está aparentemente configurado para eliminar bloques de documentos en línea. Esto hará que varias aplicaciones principales estén inaccesibles.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Probablemente esto venga a causa de la caché o un acelerador, tales como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Los módulos PHP se han instalado, pero aparecen listados como si faltaran", "Please ask your server administrator to restart the web server." : "Consulte al administrador de su servidor para reiniciar el servidor web.", "PostgreSQL >= 9 required" : "PostgreSQL 9 o superior requerido.", "Please upgrade your database version" : "Actualice su versión de base de datos.", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor cambie los permisos a 0770 para que el directorio no se pueda mostrar para otros usuarios.", "Your data directory is readable by other users" : "Su directorio data es leible por otros usuarios", "Your data directory must be an absolute path" : "Su directorio data debe ser una ruta absoluta", "Check the value of \"datadirectory\" in your configuration" : "Compruebe el valor de \"datadirectory\" en su configuración.", "Your data directory is invalid" : "Su directorio de datos es inválido", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Asegúrate de que existe un archivo llamado \".ocdata\" en la raíz del directorio de datos.", "Could not obtain lock type %d on \"%s\"." : "No se pudo realizar el bloqueo %d en \"%s\".", "Storage unauthorized. %s" : "Almacenamiento no autorizado. %s", "Storage incomplete configuration. %s" : "Configuración de almacenamiento incompleta. %s", "Storage connection error. %s" : "Error de conexión de almacenamiento. %s", "Storage is temporarily not available" : "El almacenamiento no esta disponible temporalmente", "Storage connection timeout. %s" : "Tiempo de conexión de almacenamiento agotado. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Esto puede solucionarse fácilmente %sotorgándole permisos de escritura al directorio de configuración%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Módulo con id: %s no existe. Por favor habilítelo en los ajustes de sus aplicaciones o contáctese con su administrador.", "Server settings" : "Configuración del servidor", "DB Error: \"%s\"" : "Error BD: \"%s\"", "Offending command was: \"%s\"" : "Comando infractor: \"%s\"", "You need to enter either an existing account or the administrator." : "Tiene que ingresar una cuenta existente o la del administrador.", "Offending command was: \"%s\", name: %s, password: %s" : "Comando infractor: \"%s\", nombre: %s, contraseña: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "La configuración de permisos para %s ha fallado, ya que los permisos superan los permisos dados a %s", "Setting permissions for %s failed, because the item was not found" : "La configuración de permisos para %s ha fallado, ya que no se encontró el elemento ", "Cannot clear expiration date. Shares are required to have an expiration date." : "No se puede eliminar la fecha de caducidad. Los archivos compartidos deben tener una fecha de caducidad.", "Cannot increase permissions of %s" : "No se pueden incrementar los permisos de %s", "Files can't be shared with delete permissions" : "Los archivos no pueden ser compartidos con permisos de borrado", "Files can't be shared with create permissions" : "Los arhivos no pueden ser compartidos con permisos de creación", "Cannot set expiration date more than %s days in the future" : "No se puede fijar la fecha de caducidad más de %s días en el futuro.", "Personal" : "Personal", "Admin" : "Administración", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Esto puede solucionarse fácilmente %sdándole permisos de escritura al servidor en el directorio%s de apps o deshabilitando la tienda de apps en el archivo de configuración.", "Cannot create \"data\" directory (%s)" : "No puedo crear del directorio \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Normalmente esto se puede solucionar <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">dándole al servidor web permisos de escritura en todo el directorio o el directorio 'root'</a>", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Los permisos normalmente puede solucionarse %sdándole al servidor permisos de escritura del directorio raíz%s.", "Data directory (%s) is readable by other users" : "El directorio de datos (%s) se puede leer por otros usuarios.", "Data directory (%s) must be an absolute path" : "El directorio de datos (%s) debe ser una ruta absoluta", "Data directory (%s) is invalid" : "El directorio de datos (%s) no es válido", "Please check that the data directory contains a file \".ocdata\" in its root." : "Verifique que el directorio de datos contiene un archivo \".ocdata\" en su directorio raíz." }, "nplurals=2; plural=(n != 1);"); l10n/fi.js 0000604 00000043633 15247130447 0006266 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Hakemistoon \"config\" kirjoittaminen ei onnistu!", "This can usually be fixed by giving the webserver write access to the config directory" : "Tämän voi yleensä korjata antamalla http-palvelimelle kirjoitusoikeuden asetushakemistoon", "See %s" : "Katso %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Sovelluksen %$1s tiedostoja ei vaihdettu oikein. Varmista että sen versio on yhteensopiva palvelimen kanssa.", "Sample configuration detected" : "Esimerkkimääritykset havaittu", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "On havaittu, että esimerkkimäärityksen on kopioitu. Se voi rikkoa asennuksesi, eikä sitä tueta. Lue ohjeet ennen kuin muutat config.php tiedostoa.", "%1$s and %2$s" : "%1$s ja %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s ja %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s ja %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s ja %5$s", "PHP %s or higher is required." : "PHP %s tai sitä uudempi vaaditaan.", "PHP with a version lower than %s is required." : "PHP versiota %s alempi tarvitaan.", "%sbit or higher PHP required." : "%s-bit tai korkeampi PHP vaaditaan.", "Following databases are supported: %s" : "Seuraavat tietokannat ovat tuettuja: %s", "The command line tool %s could not be found" : "Komentorivityökalua %s ei löytynyt", "The library %s is not available." : "Kirjastoa %s ei ole käytettävissä.", "Library %s with a version higher than %s is required - available version %s." : "Kirjasto %s versiota %s tai uudempi vaaditaan - käytettävissä oleva versio %s.", "Library %s with a version lower than %s is required - available version %s." : "Kirjasto %s versiota alempi %s tarvitaan - käytettävissä oleva versio %s.", "Following platforms are supported: %s" : "Seuraavat alustat ovat tuettuja: %s", "Server version %s or higher is required." : "Palvelinversio %s tai sitä uudempi vaaditaan.", "Server version %s or lower is required." : "Palvelinversio %s tai alhaisempi vaaditaan.", "Unknown filetype" : "Tuntematon tiedostotyyppi", "Invalid image" : "Virheellinen kuva", "Avatar image is not square" : "Avatar-kuva ei ole neliö", "today" : "tänään", "yesterday" : "eilen", "_%n day ago_::_%n days ago_" : ["%n päivä sitten","%n päivää sitten"], "last month" : "viime kuussa", "_%n month ago_::_%n months ago_" : ["%n kuukausi sitten","%n kuukautta sitten"], "last year" : "viime vuonna", "_%n year ago_::_%n years ago_" : ["%n vuosi sitten","%n vuotta sitten"], "_%n hour ago_::_%n hours ago_" : ["%n tunti sitten","%n tuntia sitten"], "_%n minute ago_::_%n minutes ago_" : ["%n minuutti sitten","%n minuuttia sitten"], "seconds ago" : "sekunteja sitten", "File name is a reserved word" : "Tiedoston nimi on varattu sana", "File name contains at least one invalid character" : "Tiedoston nimi sisältää ainakin yhden virheellisen merkin", "File name is too long" : "Tiedoston nimi on liian pitkä", "Dot files are not allowed" : "Pistetiedostot eivät ole sallittuja", "Empty filename is not allowed" : "Tiedostonimi ei voi olla tyhjä", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Sovellusta \"%s\" ei voi asentaa, koska appinfo-tiedostoa ei voi loi lukea.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Sovellusta \"%s\" ei voi asentaa, koska se ei ole yhteensopiva tämän palvelinversion kanssa.", "This is an automatically sent email, please do not reply." : "Tämä on automaattisesti lähetetty viesti. Älä vastaa tähän viestiin.", "Help" : "Ohje", "Apps" : "Sovellukset", "Settings" : "Asetukset", "Log out" : "Kirjaudu ulos", "Users" : "Käyttäjät", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Perusasetukset", "Sharing" : "Jakaminen", "Security" : "Turvallisuus", "Encryption" : "Salaus", "Additional settings" : "Lisäasetukset", "Tips & tricks" : "Vinkkejä", "Personal info" : "Henkilökohtaiset tiedot", "Sync clients" : "Synkronointisovellukset", "Unlimited" : "Rajoittamaton", "__language_name__" : "suomi", "%s enter the database username and name." : "%s anna tietokannan käyttäjätunnus ja nimi.", "%s enter the database username." : "%s anna tietokannan käyttäjätunnus.", "%s enter the database name." : "%s anna tietokannan nimi.", "%s you may not use dots in the database name" : "%s et voi käyttää pisteitä tietokannan nimessä", "Oracle connection could not be established" : "Oracle-yhteyttä ei voitu muodostaa", "Oracle username and/or password not valid" : "Oraclen käyttäjätunnus ja/tai salasana on väärin", "PostgreSQL username and/or password not valid" : "PostgreSQL:n käyttäjätunnus ja/tai salasana on väärin", "You need to enter details of an existing account." : "Anna olemassa olevan tilin tiedot.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X ei ole tuettu, joten %s ei toimi kunnolla tällä alustalla. Käytä omalla vastuulla!", "For the best results, please consider using a GNU/Linux server instead." : "Käytä parhaan lopputuloksen saamiseksi GNU/Linux-palvelinta.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Vaikuttaa siltä, että tämä %s-instanssi toimii 32-bittisessä PHP-ympäristössä ja open_basedir-asetus on määritetty php.ini-tiedostossa. Tämä johtaa ongelmiin yli 4 gigatavun tiedostojen kanssa, eikä siksi ole suositeltavaa.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Poista open_basedir-asetus php.ini-tiedostosta tai vaihda 64-bittiseen PHP:hen.", "Set an admin username." : "Aseta ylläpitäjän käyttäjätunnus.", "Set an admin password." : "Aseta ylläpitäjän salasana.", "Can't create or write into the data directory %s" : "Ei voi luoda tai kirjoittaa data-hakemistoon %s", "Invalid Federated Cloud ID" : "Virheellinen federoidun pilven tunniste", "Sharing %s failed, because the backend does not allow shares from type %i" : "Kohteen %s jakaminen epäonnistui, koska tietovarasto ei salli %i tyyppisiä jakoja", "Sharing %s failed, because the file does not exist" : "Kohteen %s jakaminen epäonnistui, koska tiedostoa ei ole olemassa", "You are not allowed to share %s" : "Oikeutesi eivät riitä kohteen %s jakamiseen.", "Sharing %s failed, because you can not share with yourself" : "Kohteen %s jakaminen epäonnistui, koska et voi jakaa itsesi kanssa", "Sharing %s failed, because the user %s does not exist" : "Kohteen %s jakaminen epäonnistui, koska käyttäjää %s ei ole olemassa", "Sharing %s failed, because this item is already shared with %s" : "Kohteen %s jakaminen epäonnistui, koska kohde on jo jaettu käyttäjän %s kanssa", "Sharing %s failed, because this item is already shared with user %s" : "Kohteen %s jakaminen epäonnistui, koska kohde on jo jaettu käyttäjän %s kanssa", "Sharing %s failed, because the group %s does not exist" : "Kohteen %s jakaminen epäonnistui, koska ryhmää %s ei ole olemassa", "Sharing %s failed, because %s is not a member of the group %s" : "Kohteen %s jakaminen epäonnistui, koska käyttäjä %s ei ole ryhmän %s jäsen", "You need to provide a password to create a public link, only protected links are allowed" : "Anna salasana luodaksesi julkisen linkin. Vain suojatut linkit ovat sallittuja", "Sharing %s failed, because sharing with links is not allowed" : "Kohteen %s jakaminen epäonnistui, koska jakaminen linkkejä käyttäen ei ole sallittu", "Not allowed to create a federated share with the same user" : "Saman käyttäjän kanssa ei ole sallittua luoda federoitua jakoa", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Kohteen %s jakaminen epäonnistui, kohdetta %s ei löytynyt. Kenties palvelin ei ole juuri nyt tavoitettavissa.", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Vanhenemispäivää ei voi asettaa. Jako ei voi vanhentua myöhemmin kuin %s päivää sen jälkeen kun se on jaettu", "Cannot set expiration date. Expiration date is in the past" : "Vanhenemispäivää ei voi asettaa. Vanhenemispäivä on jo mennyt", "Sharing backend %s not found" : "Jakamisen taustaosaa %s ei löytynyt", "Sharing backend for %s not found" : "Jakamisen taustaosaa kohteelle %s ei löytynyt", "Sharing failed, because the user %s is the original sharer" : "Jakaminen epäonnistui, koska käyttäjä %s ei ole alkuperäinen jakaja", "Sharing %s failed, because resharing is not allowed" : "Kohteen %s jakaminen epäonnistui, koska jakaminen uudelleen ei ole sallittu", "Sharing %s failed, because the file could not be found in the file cache" : "Kohteen %s jakaminen epäonnistui, koska tiedostoa ei löytynyt tiedostovälimuistista", "Expiration date is in the past" : "Vanhenemispäivä on menneisyydessä", "%s shared »%s« with you" : "%s jakoi kohteen »%s« kanssasi", "Could not find category \"%s\"" : "Luokkaa \"%s\" ei löytynyt", "Sunday" : "sunnuntai", "Monday" : "maanantai", "Tuesday" : "tiistai", "Wednesday" : "keskiviikko", "Thursday" : "torstai", "Friday" : "perjantai", "Saturday" : "lauantai", "Sun." : "Su", "Mon." : "Ma", "Tue." : "Ti", "Wed." : "Ke", "Thu." : "To", "Fri." : "Pe", "Sat." : "La", "Su" : "Su", "Mo" : "Ma", "Tu" : "Ti", "We" : "Ke", "Th" : "To", "Fr" : "Pe", "Sa" : "La", "January" : "tammikuu", "February" : "helmikuu", "March" : "maaliskuu", "April" : "huhtikuu", "May" : "toukokuu", "June" : "kesäkuu", "July" : "heinäkuu", "August" : "elokuu", "September" : "syyskuu", "October" : "lokakuu", "November" : "marraskuu", "December" : "joulukuu", "Jan." : "Tammi", "Feb." : "Helmi", "Mar." : "Maalis", "Apr." : "Huhti", "May." : "Touko", "Jun." : "Kesä", "Jul." : "Heinä", "Aug." : "Elo", "Sep." : "Syys", "Oct." : "Loka", "Nov." : "Marras", "Dec." : "Joulu", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Vain seuraavat merkit ovat sallittuja käyttäjätunnuksessa: \"a-z\", \"A-Z\", \"0-9\" ja \"_.@-'\"", "A valid username must be provided" : "Anna kelvollinen käyttäjätunnus", "Username contains whitespace at the beginning or at the end" : "Käyttäjätunnus sisältää tyhjätilaa joko alussa tai lopussa", "Username must not consist of dots only" : "Käyttäjänimi ei voi koostua vain pisteistä", "A valid password must be provided" : "Anna kelvollinen salasana", "The username is already being used" : "Käyttäjätunnus on jo käytössä", "User disabled" : "Käyttäjä poistettu käytöstä", "Login canceled by app" : "Kirjautuminen peruttiin sovelluksen toimesta", "No app name specified" : "Sovelluksen nimeä ei määritelty", "App '%s' could not be installed!" : "Sovellusta \"%s\" ei voi asentaa!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Sovelluksen \"%s\" asennus ei onnistu, koska seuraavia riippuvuuksia ei ole täytetty: %s", "a safe home for all your data" : "turvallinen koti kaikille tiedostoillesi", "File is currently busy, please try again later" : "Tiedosto on parhaillaan käytössä, yritä myöhemmin uudelleen", "Can't read file" : "Tiedostoa ei voi lukea", "Application is not enabled" : "Sovellusta ei ole otettu käyttöön", "Authentication error" : "Tunnistautumisvirhe", "Token expired. Please reload page." : "Valtuutus vanheni. Lataa sivu uudelleen.", "Unknown user" : "Tuntematon käyttäjä", "No database drivers (sqlite, mysql, or postgresql) installed." : "Tietokanta-ajureita (sqlite, mysql tai postgresql) ei ole asennettu.", "Cannot write into \"config\" directory" : "Hakemistoon \"config\" kirjoittaminen ei onnistu", "Cannot write into \"apps\" directory" : "Hakemistoon \"apps\" kirjoittaminen ei onnistu", "Cannot create \"data\" directory" : "Hakemiston \"data\" luominen ei onnistu", "Setting locale to %s failed" : "Maa-asetuksen %s asettaminen epäonnistui", "Please install one of these locales on your system and restart your webserver." : "Asenna ainakin yksi kyseisistä maa-asetuksista järjestelmään ja käynnistä http-palvelin uudelleen.", "Please ask your server administrator to install the module." : "Pyydä palvelimen ylläpitäjää asentamaan moduulin.", "PHP module %s not installed." : "PHP-moduulia %s ei ole asennettu.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-asetusta \"%s\" ei ole asetettu arvoon \"%s\".", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload on asetettu arvoon \"%s\" odotetun arvon \"0\" sijaan", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Korjaa tämä ongelma asettamalla <code>mbstring.func_overload</code> arvoon <code>0</code> php.ini-tiedostossasi", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Vähintään libxml2 2.7.0 vaaditaan. %s on asennettu.", "To fix this issue update your libxml2 version and restart your web server." : "Päivitä libxml2:n versio ja käynnistä http-palvelin uudelleen.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Tämä johtuu todennäköisesti välimuistista tai kiihdyttimestä kuten Zend OPcachesta tai eAcceleratorista.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-moduulit on asennettu, mutta ovatko ne vieläkin listattu puuttuviksi?", "Please ask your server administrator to restart the web server." : "Pyydä palvelimen ylläpitäjää käynnistämään web-palvelin uudelleen.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 vaaditaan", "Please upgrade your database version" : "Päivitä tietokantasi versio", "Your data directory is readable by other users" : "Data-hakemisto on muiden käyttäjien luettavissa", "Your data directory must be an absolute path" : "Data-hakemiston tulee olla absoluuttinen polku", "Check the value of \"datadirectory\" in your configuration" : "Tarkista \"datadirectory\"-arvo asetuksistasi", "Your data directory is invalid" : "Datahakemistosi on virheellinen", "Could not obtain lock type %d on \"%s\"." : "Lukitustapaa %d ei saatu kohteelle \"%s\".", "Storage unauthorized. %s" : "Tallennustila ei ole valtuutettu. %s", "Storage incomplete configuration. %s" : "Tallennustilan puutteellinen määritys. %s", "Storage connection error. %s" : "Tallennustilan yhteysvirhe. %s", "Storage is temporarily not available" : "Tallennustila on tilapäisesti pois käytöstä", "Storage connection timeout. %s" : "Tallennustilan yhteyden aikakatkaisu. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Tämän voi yleensä korjata antamalla %shttp-palvelimelle kirjoitusoikeuden asetushakemistoon%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduulia tunnisteella %s ei ole olemassa. Ota se käyttöön sovellusasetuksista tai ota yhteys ylläpitoon.", "Server settings" : "Palvelimen asetukset", "DB Error: \"%s\"" : "Tietokantavirhe: \"%s\"", "Offending command was: \"%s\"" : "Loukkaava komento oli: \"%s\"", "You need to enter either an existing account or the administrator." : "Sinun täytyy antaa joko olemassa oleva tili tai ylläpitäjä.", "Offending command was: \"%s\", name: %s, password: %s" : "Loukkaava komento oli: \"%s\", nimi: %s, salasana: %s", "Setting permissions for %s failed, because the item was not found" : "Kohteen %s oikeuksien asettaminen epäonnistui, koska kohdetta ei löytynyt", "Cannot clear expiration date. Shares are required to have an expiration date." : "Vanhenemispäivän tyhjentäminen ei onnistu. Jaoille on määritelty pakolliseksi vanhenemispäivä.", "Cannot increase permissions of %s" : "Kohteen %s käyttöoikeuksien lisääminen ei onnistu", "Files can't be shared with delete permissions" : "Tiedostoja ei voi jakaa poistamisoikeusilla", "Files can't be shared with create permissions" : "Tiedostoja ei voi jakaa luomisoikeuksilla", "Cannot set expiration date more than %s days in the future" : "Vanhenemispäivä voi olla korkeintaan %s päivän päässä tulevaisuudessa", "Personal" : "Henkilökohtainen", "Admin" : "Ylläpito", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Tämä on yleensä mahdollista korjata %santamalla HTTP-palvelimelle kirjoitusoikeus sovellushakemistoon%s tai poistamalla sovelluskauppa pois käytöstä asetustiedostoa käyttäen.", "Cannot create \"data\" directory (%s)" : "Hakemiston \"data\" luominen ei onnistu (%s)", "Data directory (%s) is readable by other users" : "Data-hakemisto (%s) on muiden käyttäjien luettavissa", "Data directory (%s) must be an absolute path" : "Data-hakemiston (%s) tulee olla absoluuttinen polku", "Data directory (%s) is invalid" : "Data-hakemisto (%s) on virheellinen", "Please check that the data directory contains a file \".ocdata\" in its root." : "Varmista, että data-hakemiston juuressa on tiedosto \".ocdata\"." }, "nplurals=2; plural=(n != 1);"); l10n/en_GB.js 0000604 00000052213 15247130447 0006634 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Cannot write into \"config\" directory!", "This can usually be fixed by giving the webserver write access to the config directory" : "This can usually be fixed by giving the webserver write access to the config directory", "See %s" : "See %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "This can usually be fixed by giving the webserver write access to the config directory. See %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.", "Sample configuration detected" : "Sample configuration detected", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php", "%1$s and %2$s" : "%1$s and %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s and %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s and %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s and %5$s", "Education Edition" : "Education Edition", "Enterprise bundle" : "Enterprise bundle", "Groupware bundle" : "Groupware bundle", "Social sharing bundle" : "Social sharing bundle", "PHP %s or higher is required." : "PHP %s or higher is required.", "PHP with a version lower than %s is required." : "PHP with a version lower than %s is required.", "%sbit or higher PHP required." : "%sbit or higher PHP required.", "Following databases are supported: %s" : "Following databases are supported: %s", "The command line tool %s could not be found" : "The command line tool %s could not be found", "The library %s is not available." : "The library %s is not available.", "Library %s with a version higher than %s is required - available version %s." : "Library %s with a version higher than %s is required - available version %s.", "Library %s with a version lower than %s is required - available version %s." : "Library %s with a version lower than %s is required - available version %s.", "Following platforms are supported: %s" : "Following platforms are supported: %s", "Server version %s or higher is required." : "Server version %s or higher is required.", "Server version %s or lower is required." : "Server version %s or lower is required.", "Unknown filetype" : "Unknown filetype", "Invalid image" : "Invalid image", "Avatar image is not square" : "Avatar image is not square", "today" : "today", "yesterday" : "yesterday", "_%n day ago_::_%n days ago_" : ["%n day ago","%n days ago"], "last month" : "last month", "_%n month ago_::_%n months ago_" : ["%n month ago","%n months ago"], "last year" : "last year", "_%n year ago_::_%n years ago_" : ["%n year ago","%n years ago"], "_%n hour ago_::_%n hours ago_" : ["%n hour ago","%n hours ago"], "_%n minute ago_::_%n minutes ago_" : ["%n minute ago","%n minutes ago"], "seconds ago" : "seconds ago", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator.", "File name is a reserved word" : "File name is a reserved word", "File name contains at least one invalid character" : "File name contains at least one invalid character", "File name is too long" : "File name is too long", "Dot files are not allowed" : "Dot files are not allowed", "Empty filename is not allowed" : "Empty filename is not allowed", "App \"%s\" cannot be installed because appinfo file cannot be read." : "App \"%s\" cannot be installed because appinfo file cannot be read.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "App \"%s\" cannot be installed. It is not compatible with this version of the server.", "This is an automatically sent email, please do not reply." : "This is an automatically sent email, please do not reply.", "Help" : "Help", "Apps" : "Apps", "Settings" : "Settings", "Log out" : "Log out", "Users" : "Users", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Basic settings", "Sharing" : "Sharing", "Security" : "Security", "Encryption" : "Encryption", "Additional settings" : "Additional settings", "Tips & tricks" : "Tips & tricks", "Personal info" : "Personal info", "Sync clients" : "Sync clients", "Unlimited" : "Unlimited", "__language_name__" : "__language_name__", "Verifying" : "Verifying", "Verifying …" : "Verifying …", "Verify" : "Verify", "%s enter the database username and name." : "%s enter the database username and name.", "%s enter the database username." : "%s enter the database username.", "%s enter the database name." : "%s enter the database name.", "%s you may not use dots in the database name" : "%s you may not use dots in the database name", "Oracle connection could not be established" : "Oracle connection could not be established", "Oracle username and/or password not valid" : "Oracle username and/or password not valid", "PostgreSQL username and/or password not valid" : "PostgreSQL username and/or password not valid", "You need to enter details of an existing account." : "You need to enter details of an existing account.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! ", "For the best results, please consider using a GNU/Linux server instead." : "For the best results, please consider using a GNU/Linux server instead.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir setting has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP.", "Set an admin username." : "Set an admin username.", "Set an admin password." : "Set an admin password.", "Can't create or write into the data directory %s" : "Can't create or write into the data directory %s", "Invalid Federated Cloud ID" : "Invalid Federated Cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Sharing %s failed, because the backend does not allow shares from type %i", "Sharing %s failed, because the file does not exist" : "Sharing %s failed, because the file does not exist", "You are not allowed to share %s" : "You are not allowed to share %s", "Sharing %s failed, because you can not share with yourself" : "Sharing %s failed, because you can not share with yourself", "Sharing %s failed, because the user %s does not exist" : "Sharing %s failed, because the user %s does not exist", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of", "Sharing %s failed, because this item is already shared with %s" : "Sharing %s failed, because this item is already shared with %s", "Sharing %s failed, because this item is already shared with user %s" : "Sharing %s failed, because this item is already shared with user %s", "Sharing %s failed, because the group %s does not exist" : "Sharing %s failed, because the group %s does not exist", "Sharing %s failed, because %s is not a member of the group %s" : "Sharing %s failed, because %s is not a member of the group %s", "You need to provide a password to create a public link, only protected links are allowed" : "You need to provide a password to create a public link, only protected links are allowed", "Sharing %s failed, because sharing with links is not allowed" : "Sharing %s failed, because sharing with links is not allowed", "Not allowed to create a federated share with the same user" : "Not allowed to create a federated share with the same user", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Sharing %s failed, could not find %s, maybe the server is currently unreachable.", "Share type %s is not valid for %s" : "Share type %s is not valid for %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Cannot set expiry date. Shares cannot expire later than %s after they have been shared", "Cannot set expiration date. Expiration date is in the past" : "Cannot set expiry date. Expiry date is in the past", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Sharing backend %s must implement the interface OCP\\Share_Backend", "Sharing backend %s not found" : "Sharing backend %s not found", "Sharing backend for %s not found" : "Sharing backend for %s not found", "Sharing failed, because the user %s is the original sharer" : "Sharing failed, because the user %s is the original sharer", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Sharing %s failed, because the permissions exceed permissions granted to %s", "Sharing %s failed, because resharing is not allowed" : "Sharing %s failed, because resharing is not allowed", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Sharing %s failed, because the sharing backend for %s could not find its source", "Sharing %s failed, because the file could not be found in the file cache" : "Sharing %s failed, because the file could not be found in the file cache", "Can’t increase permissions of %s" : "Can’t increase permissions of %s", "Files can’t be shared with delete permissions" : "Files can’t be shared with delete permissions", "Files can’t be shared with create permissions" : "Files can’t be shared with create permissions", "Expiration date is in the past" : "Expiration date is in the past", "Can’t set expiration date more than %s days in the future" : "Can’t set expiration date more than %s days in the future", "%s shared »%s« with you" : "%s shared \"%s\" with you", "%s shared »%s« with you." : "%s shared »%s« with you.", "Click the button below to open it." : "Click the button below to open it.", "Open »%s«" : "Open »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "The requested share does not exist anymore", "Could not find category \"%s\"" : "Could not find category \"%s\"", "Sunday" : "Sunday", "Monday" : "Monday", "Tuesday" : "Tuesday", "Wednesday" : "Wednesday", "Thursday" : "Thursday", "Friday" : "Friday", "Saturday" : "Saturday", "Sun." : "Sun.", "Mon." : "Mon.", "Tue." : "Tue.", "Wed." : "Wed.", "Thu." : "Thu.", "Fri." : "Fri.", "Sat." : "Sat.", "Su" : "Su", "Mo" : "Mo", "Tu" : "Tu", "We" : "We", "Th" : "Th", "Fr" : "Fr", "Sa" : "Sa", "January" : "January", "February" : "February", "March" : "March", "April" : "April", "May" : "May", "June" : "June", "July" : "July", "August" : "August", "September" : "September", "October" : "October", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "May.", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "A valid username must be provided", "Username contains whitespace at the beginning or at the end" : "Username contains whitespace at the beginning or at the end", "Username must not consist of dots only" : "Username must not consist of dots only", "A valid password must be provided" : "A valid password must be provided", "The username is already being used" : "The username is already being used", "Could not create user" : "Could not create user", "User disabled" : "User disabled", "Login canceled by app" : "Login cancelled by app", "No app name specified" : "No app name specified", "App '%s' could not be installed!" : "App '%s' could not be installed!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s", "a safe home for all your data" : "a safe home for all your data", "File is currently busy, please try again later" : "File is currently busy, please try again later", "Can't read file" : "Can't read file", "Application is not enabled" : "Application is not enabled", "Authentication error" : "Authentication error", "Token expired. Please reload page." : "Token expired. Please reload page.", "Unknown user" : "Unknown user", "No database drivers (sqlite, mysql, or postgresql) installed." : "No database drivers (sqlite, mysql, or postgresql) installed.", "Cannot write into \"config\" directory" : "Cannot write into \"config\" directory", "Cannot write into \"apps\" directory" : "Cannot write into \"apps\" directory", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s", "Cannot create \"data\" directory" : "Cannot create \"data\" directory", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "This can usually be fixed by giving the webserver write access to the root directory. See %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s.", "Setting locale to %s failed" : "Setting locale to %s failed", "Please install one of these locales on your system and restart your webserver." : "Please install one of these locales on your system and restart your webserver.", "Please ask your server administrator to install the module." : "Please ask your server administrator to install the module.", "PHP module %s not installed." : "PHP module %s not installed.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP setting \"%s\" is not set to \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Adjusting this setting in php.ini will allow Nextcloud to run", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 is at least required. Currently %s is installed.", "To fix this issue update your libxml2 version and restart your web server." : "To fix this issue update your libxml2 version and restart your web server.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP modules have been installed, but they are still listed as missing?", "Please ask your server administrator to restart the web server." : "Please ask your server administrator to restart the web server.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 required", "Please upgrade your database version" : "Please upgrade your database version", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Please change the permissions to 0770 so that the directory cannot be listed by other users.", "Your data directory is readable by other users" : "Your data directory is readable by other users", "Your data directory must be an absolute path" : "Your data directory must be an absolute path", "Check the value of \"datadirectory\" in your configuration" : "Check the value of \"datadirectory\" in your configuration", "Your data directory is invalid" : "Your data directory is invalid", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Ensure there is a file called \".ocdata\" in the root of the data directory.", "Could not obtain lock type %d on \"%s\"." : "Could not obtain lock type %d on \"%s\".", "Storage unauthorized. %s" : "Storage unauthorised. %s", "Storage incomplete configuration. %s" : "Storage incomplete configuration. %s", "Storage connection error. %s" : "Storage connection error. %s", "Storage is temporarily not available" : "Storage is temporarily not available", "Storage connection timeout. %s" : "Storage connection timeout. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "This can usually be fixed by %sgiving the webserver write access to the config directory%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator.", "Server settings" : "Server settings", "DB Error: \"%s\"" : "DB Error: \"%s\"", "Offending command was: \"%s\"" : "Offending command was: \"%s\"", "You need to enter either an existing account or the administrator." : "You need to enter either an existing account or the administrator.", "Offending command was: \"%s\", name: %s, password: %s" : "Offending command was: \"%s\", name: %s, password: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Setting permissions for %s failed, because the permissions exceed permissions granted to %s", "Setting permissions for %s failed, because the item was not found" : "Setting permissions for %s failed, because the item was not found", "Cannot clear expiration date. Shares are required to have an expiration date." : "Cannot clear expiration date. Shares are required to have an expiration date.", "Cannot increase permissions of %s" : "Cannot increase permissions of %s", "Files can't be shared with delete permissions" : "Files can't be shared with delete permissions", "Files can't be shared with create permissions" : "Files can't be shared with create permissions", "Cannot set expiration date more than %s days in the future" : "Cannot set expiration date more than %s days in the future", "Personal" : "Personal", "Admin" : "Admin", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file.", "Cannot create \"data\" directory (%s)" : "Cannot create \"data\" directory (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s.", "Data directory (%s) is readable by other users" : "Data directory (%s) is readable by other users", "Data directory (%s) must be an absolute path" : "Data directory (%s) must be an absolute path", "Data directory (%s) is invalid" : "Data directory (%s) is invalid", "Please check that the data directory contains a file \".ocdata\" in its root." : "Please check that the data directory contains a file \".ocdata\" in its root." }, "nplurals=2; plural=(n != 1);"); l10n/zh_CN.js 0000604 00000047577 15247130447 0006704 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "无法写入 \"config\" 目录!ond", "This can usually be fixed by giving the webserver write access to the config directory" : "您可以设置 Web 服务器对 config 目录的写权限修复这个问题", "See %s" : "查看 %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "这个通常可以通过赋予写入权限到 config 目录来修复。查看:%s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "应用 %$1s 的文件替换不正确. 请确认版本与当前服务器兼容.", "Sample configuration detected" : "示例配置检测", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "您似乎直接把 config.php 的样例文件直接复制使用. 这可能会破坏您的安装. 在对 config.php 进行修改之前请先阅读相关文档.", "%1$s and %2$s" : "%1$s 和 %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s 和 %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s 和 %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s 和 %5$s", "Education Edition" : "教育版", "Enterprise bundle" : "企业捆绑包", "Groupware bundle" : "群组捆绑包", "Social sharing bundle" : "社交分享捆绑包", "PHP %s or higher is required." : "要求 PHP 版本 %s 或者更高。", "PHP with a version lower than %s is required." : "需要版本低于 %s 的PHP.", "%sbit or higher PHP required." : "需要 %s 或更高版本的 PHP", "Following databases are supported: %s" : "支持以下数据库: %s", "The command line tool %s could not be found" : "命令行工具 %s 未找到", "The library %s is not available." : "库文件 %s 不可用", "Library %s with a version higher than %s is required - available version %s." : "%s 需要 %s 或更高的版本 - 可用版本 %s.", "Library %s with a version lower than %s is required - available version %s." : "%s 需要 %s 或更低的版本 - 可用版本 %s.", "Following platforms are supported: %s" : "支持以下平台:%s", "Server version %s or higher is required." : "需要服务器版本 %s 或更高版本。", "Server version %s or lower is required." : "需要服务器版本 %s 或更低版本。", "Unknown filetype" : "未知的文件类型", "Invalid image" : "无效的图像", "Avatar image is not square" : "头像图像不是正方形", "today" : "今天", "yesterday" : "昨天", "_%n day ago_::_%n days ago_" : ["%n 天前"], "last month" : "上月", "_%n month ago_::_%n months ago_" : ["%n 月前"], "last year" : "去年", "_%n year ago_::_%n years ago_" : ["%n 年前"], "_%n hour ago_::_%n hours ago_" : ["%n 小时前"], "_%n minute ago_::_%n minutes ago_" : ["%n 分钟前"], "seconds ago" : "几秒前", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "模块:%s不存在。请在 App 设置中开启或联系管理员。", "File name is a reserved word" : "文件名包含敏感字符", "File name contains at least one invalid character" : "文件名中存在至少一个非法字符", "File name is too long" : "文件名过长", "Dot files are not allowed" : ".文件 不被允许", "Empty filename is not allowed" : "不允许使用空名称。", "App \"%s\" cannot be installed because appinfo file cannot be read." : "无法安装应用\"%s\",因为无法读取appinfo文件.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "应用程式 \"%s\" 无法安装,因为它与这个版本的服务器不兼容.", "This is an automatically sent email, please do not reply." : "这是一个自动生成的电子邮件,请不要回复。", "Help" : "帮助", "Apps" : "应用", "Settings" : "设置", "Log out" : "注销", "Users" : "用户", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "基本设置", "Sharing" : "分享", "Security" : "安全", "Encryption" : "加密", "Additional settings" : "其他设置", "Tips & tricks" : "小提示", "Personal info" : "个人信息", "Sync clients" : "同步客户", "Unlimited" : "无限制", "__language_name__" : "简体中文", "Verifying" : "验证", "Verifying …" : "验证...", "Verify" : "验证", "%s enter the database username and name." : "%s 输入数据库用户名和名称.", "%s enter the database username." : "%s 输入数据库用户名。", "%s enter the database name." : "%s 输入数据库名称。", "%s you may not use dots in the database name" : "%s 您不能在数据库名称中使用英文句号。", "Oracle connection could not be established" : "不能建立甲骨文连接", "Oracle username and/or password not valid" : "Oracle 数据库用户名和/或密码无效", "PostgreSQL username and/or password not valid" : "PostgreSQL 数据库用户名和/或密码无效", "You need to enter details of an existing account." : "您需要输入现有帐户的详细信息。", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X 不被支持并且 %s 在这个平台上无法正常工作。请自行承担风险!", "For the best results, please consider using a GNU/Linux server instead." : "为了达到最好的效果,请考虑使用 GNU/Linux 服务器。", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "看起来这个 %s 实例运行在32位PHP环境中并且已在php.ini中配置open_basedir。这将在文件超过4GB时出现问题,我们极力反对这样做。", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "请删除php.ini中的open_basedir设置或切换到64位PHP。", "Set an admin username." : "请设置一个管理员用户名。", "Set an admin password." : "请设置一个管理员密码。", "Can't create or write into the data directory %s" : "无法创建或写入数据目录 %s", "Invalid Federated Cloud ID" : "无效的联合云ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "分享 %s 失败, 因为后端不允许分享 %i 类型", "Sharing %s failed, because the file does not exist" : "分享 %s 失败, 因为文件不存在.", "You are not allowed to share %s" : "您无权分享 %s", "Sharing %s failed, because you can not share with yourself" : "分享 %s 失败, 因为您不能分享给自己", "Sharing %s failed, because the user %s does not exist" : "分享 %s 失败, 因为用户 %s 不存在", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "分享 %s 失败, 因为用户 %s 不是 %s 所属的任何组的用户", "Sharing %s failed, because this item is already shared with %s" : "分享 %s 失败, 因为该项已经分享给用户 %s", "Sharing %s failed, because this item is already shared with user %s" : "分享 %s 失败, 因为该项已经分享给用户 %s", "Sharing %s failed, because the group %s does not exist" : "分享 %s 失败, 因为 %s 分组不存在", "Sharing %s failed, because %s is not a member of the group %s" : "分享 %s 失败, 因为 %s 不是 %s 分组的成员", "You need to provide a password to create a public link, only protected links are allowed" : "链接分享需要密码, 您需要提供一个密码以创建公开连接", "Sharing %s failed, because sharing with links is not allowed" : "分享 %s 失败, 因为不允许使用链接分享", "Not allowed to create a federated share with the same user" : "不能给你自己分享文件", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "分享 %s 失败, 无法找到 %s, 该服务当前无法连接.", "Share type %s is not valid for %s" : "%s 不是 %s 的合法共享类型", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "无法设置过期时间. 过期时间不能晚于其分享时间 %s", "Cannot set expiration date. Expiration date is in the past" : "无法设置过期时间. 过期时间不能为过去", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "分享后端 %s 必须实现 OCP\\Share_Backend 接口", "Sharing backend %s not found" : "%s 的分享后端未找到", "Sharing backend for %s not found" : "%s 的分享后端未找到", "Sharing failed, because the user %s is the original sharer" : "分享失败, 因为用户 %s 是原始的分享者.", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "分享 %s 失败, 因为权限超过了 %s 的已有权限", "Sharing %s failed, because resharing is not allowed" : "分享 %s 失败, 因为不允许二次共享", "Sharing %s failed, because the sharing backend for %s could not find its source" : "分享 %s 失败, 因为无法找到 %s 分享后端的来源", "Sharing %s failed, because the file could not be found in the file cache" : "分享 %s 失败, 因为文件缓存中找不到该文件", "Can’t increase permissions of %s" : "无法增加%s的权限。", "Files can’t be shared with delete permissions" : "无法分享有删除权限的文件", "Files can’t be shared with create permissions" : "无法分享有创建权限的文件", "Expiration date is in the past" : "到期日期已过.", "Can’t set expiration date more than %s days in the future" : "无法将过期日期设置为超过 %s 天.", "%s shared »%s« with you" : "%s 向您分享了 »%s«", "%s shared »%s« with you." : "%s 已与您共享了 %s .", "Click the button below to open it." : "点击下方按钮可打开它.", "Open »%s«" : "打开 %s", "%s via %s" : "%s 通过 %s", "The requested share does not exist anymore" : "当前请求的共享已经不存在", "Could not find category \"%s\"" : "无法找到分类 \"%s\"", "Sunday" : "星期日", "Monday" : "星期一", "Tuesday" : "星期二", "Wednesday" : "星期三", "Thursday" : "星期四", "Friday" : "星期五", "Saturday" : "星期六", "Sun." : "周日", "Mon." : "周一", "Tue." : "周二", "Wed." : "周三", "Thu." : "周四", "Fri." : "周五", "Sat." : "周六", "Su" : "日", "Mo" : "一", "Tu" : "二", "We" : "三", "Th" : "四", "Fr" : "五", "Sa" : "六", "January" : "一月", "February" : "二月", "March" : "三月", "April" : "四月", "May" : "五月", "June" : "六月", "July" : "七月", "August" : "八月", "September" : "九月", "October" : "十月", "November" : "十一月", "December" : "十二月", "Jan." : "一月", "Feb." : "二月", "Mar." : "三月", "Apr." : "四月", "May." : "五月", "Jun." : "六月", "Jul." : "七月", "Aug." : "八月", "Sep." : "九月", "Oct." : "十月", "Nov." : "十一月", "Dec." : "十二月", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "在用户名中只允许使用以下字符:“a-z”,“A-Z”,“0-9”和\"_.@-'\"", "A valid username must be provided" : "必须提供合法的用户名", "Username contains whitespace at the beginning or at the end" : "用户名在开头或结尾处包含空格", "Username must not consist of dots only" : "用户名不能仅由点组成", "A valid password must be provided" : "必须提供合法的密码", "The username is already being used" : "用户名已被使用", "Could not create user" : "无法创建用户", "User disabled" : "用户已禁用", "Login canceled by app" : "已通过应用取消登录", "No app name specified" : "没有指定的 App 名称", "App '%s' could not be installed!" : "应用程序 '%s' 无法被安装!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "应用程序 \"%s\" 无法被安装,因为为满足下列依赖关系: %s", "a safe home for all your data" : "给您所有的数据一个安全的家", "File is currently busy, please try again later" : "文件当前正忙,请稍后再试", "Can't read file" : "无法读取文件", "Application is not enabled" : "应用程序未启用", "Authentication error" : "认证出错", "Token expired. Please reload page." : "Token 过期,请刷新页面。", "Unknown user" : "未知用户", "No database drivers (sqlite, mysql, or postgresql) installed." : "没有安装数据库驱动 (SQLite、MySQL 或 PostgreSQL)。", "Cannot write into \"config\" directory" : "无法写入“config”目录", "Cannot write into \"apps\" directory" : "无法写入“apps”目录", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "这个通常可以通过赋予 apps 目录写入权限或者在 config 文件中关闭 AppStore 来修复。详情:%s", "Cannot create \"data\" directory" : "无法创建“data”目录 ", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "这个通常可以通过赋予根目录写入权限来修复。查看:%s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "权限通常可以通过赋予根目录写入权限来修复。查看:%s。", "Setting locale to %s failed" : "设置语言为 %s 失败", "Please install one of these locales on your system and restart your webserver." : "请在您的系统中安装下述一种语言并重启 Web 服务器.", "Please ask your server administrator to install the module." : "请联系服务器管理员安装模块.", "PHP module %s not installed." : "PHP %s 模块未安装.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP 选项 \"%s\" 未设置为 \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "在 php.ini 中调整该设置将导致 Nextcloud 重新运行", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload 当前设置为 \"%s\", 预期值为 \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "请在 php.ini 中设置 <code>mbstring.func_overload</code> 为 <code>0</code> 以解决该问题", "libxml2 2.7.0 is at least required. Currently %s is installed." : "至少需要 libxml2 2.7.0. 当前安装 %s.", "To fix this issue update your libxml2 version and restart your web server." : "升级您的 libxml2 版本然后重启 Web 服务器以解决该问题.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP 被设置为移除内联块, 这将导致多个核心应用无法访问.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "这可能由缓存/加速器导致的, 例如 Zend OPcache 或 eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP 模块已经安装, 但仍然显示未安装?", "Please ask your server administrator to restart the web server." : "请联系服务器管理员重启 Web 服务器.", "PostgreSQL >= 9 required" : "要求 PostgreSQL >= 9", "Please upgrade your database version" : "请升级您的数据库版本", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "请更改权限为 0770 以避免其他用户查看目录.", "Your data directory is readable by other users" : "你的数据目录可被其他用户读取", "Your data directory must be an absolute path" : "您的数据目录必须是绝对路径", "Check the value of \"datadirectory\" in your configuration" : "请检查配置文件中 \"datadirectory\" 的值", "Your data directory is invalid" : "您的数据目录无效", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "请确定在根目录下有一个名为\".ocdata\"的文件。", "Could not obtain lock type %d on \"%s\"." : "无法在 \"%s\" 上获取锁类型 %d.", "Storage unauthorized. %s" : "存储认证失败. %s", "Storage incomplete configuration. %s" : "存储未完成配置. %s", "Storage connection error. %s" : "存储连接错误. %s", "Storage is temporarily not available" : "存储暂时不可用", "Storage connection timeout. %s" : "存储连接超时. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "您可以由 %s 设置 Web 服务器对 config 目录 %s 的写权限修复这个问题", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID 为 %s 的模块不存在. 请在应用设置中启用或联系您的管理员.", "Server settings" : "服务器设置", "DB Error: \"%s\"" : "数据库错误:\"%s\"", "Offending command was: \"%s\"" : "冲突命令为:\"%s\"", "You need to enter either an existing account or the administrator." : "你需要输入一个数据库中已有的账户或管理员账户。", "Offending command was: \"%s\", name: %s, password: %s" : "冲突命令为:\"%s\",名称:%s,密码:%s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "设置 %s 权限失败,因为权限超出了 %s 已有权限。", "Setting permissions for %s failed, because the item was not found" : "设置 %s 的权限失败,因为未找到到对应项", "Cannot clear expiration date. Shares are required to have an expiration date." : "无法清除过期时间. 每个分享必须有一个过期时间", "Cannot increase permissions of %s" : "无法提升 %s 的权限", "Files can't be shared with delete permissions" : "无法分享有删除权限的文件", "Files can't be shared with create permissions" : "无法分享有创建权限的文件", "Cannot set expiration date more than %s days in the future" : "无法将过期日期设置为超过 %s 天.", "Personal" : "个人", "Admin" : "管理", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "您可以由 %s 设置 Web 服务器对应用目录 %s 的写权限或在配置文件中禁用应用商店可以修复这个问题.", "Cannot create \"data\" directory (%s)" : "无法创建“apps”目录 (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "点击 <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">设置 Web 服务器对根目录的写入权限</a> 可修复这个问题.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "您可以由 %s 设置 Web 服务器对根目录 %s 的写权限可以修复这个问题.", "Data directory (%s) is readable by other users" : "数据目录 (%s) 能被其他用户读取", "Data directory (%s) must be an absolute path" : "数据目录 (%s) 必须为绝对路径", "Data directory (%s) is invalid" : "数据目录 (%s) 无效", "Please check that the data directory contains a file \".ocdata\" in its root." : "请检查根目录下 data 目录中包含名为 \".ocdata\" 的文件." }, "nplurals=1; plural=0;"); l10n/cs.js 0000604 00000054743 15247130447 0006301 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Nelze zapisovat do adresáře \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "To lze obvykle vyřešit povolením zápisu webovému serveru do konfiguračního adresáře", "See %s" : "Viz %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "To lze obvykle vyřešit povolením zápisu webovému serveru do konfiguračního adresáře. Viz %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Soubory aplikace %$1s nebyly řádně nahrazeny. Ujistěte se, že je to verze kompatibilní se serverem.", "Sample configuration detected" : "Byla detekována vzorová konfigurace", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Pravděpodobně byla zkopírována konfigurační nastavení ze vzorových souborů. Toto není podporováno a může poškodit vaši instalaci. Nahlédněte prosím do dokumentace před prováděním změn v souboru config.php", "%1$s and %2$s" : "%1$s a %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s a %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s a %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s a %5$s", "Education Edition" : "Edice pro výuku", "Enterprise bundle" : "Enterprise balíček", "Groupware bundle" : "Balíček groupware", "Social sharing bundle" : "Balíček sociálního sdílení", "PHP %s or higher is required." : "Je vyžadováno PHP %s nebo vyšší.", "PHP with a version lower than %s is required." : "Je vyžadováno PHP ve verzi nižší než %s.", "%sbit or higher PHP required." : "Je vyžadováno PHP %sbit nebo vyšší.", "Following databases are supported: %s" : "Jsou podporovány následující databáze: %s", "The command line tool %s could not be found" : "Nástroj příkazového řádku %s nebyl nalezen", "The library %s is not available." : "Knihovna %s není dostupná.", "Library %s with a version higher than %s is required - available version %s." : "Je vyžadována knihovna %s ve verzi vyšší než %s - dostupná verze %s.", "Library %s with a version lower than %s is required - available version %s." : "Je vyžadována knihovna %s ve verzi nižší než %s - dostupná verze %s.", "Following platforms are supported: %s" : "Jsou podporovány následující systémy: %s", "Server version %s or higher is required." : "Je potřeba verze serveru %s nebo vyšší.", "Server version %s or lower is required." : "Je potřeba verze serveru %s nebo nižší.", "Unknown filetype" : "Neznámý typ souboru", "Invalid image" : "Chybný obrázek", "Avatar image is not square" : "Avatar není čtvercový", "today" : "dnes", "yesterday" : "včera", "_%n day ago_::_%n days ago_" : ["včera","před %n dny","před %n dny"], "last month" : "minulý měsíc", "_%n month ago_::_%n months ago_" : ["před %n měsícem","před %n měsíci","před %n měsíci"], "last year" : "minulý rok", "_%n year ago_::_%n years ago_" : ["před rokem","před %n lety","před %n lety"], "_%n hour ago_::_%n hours ago_" : ["před %n hodinou","před %n hodinami","před %n hodinami"], "_%n minute ago_::_%n minutes ago_" : ["před %n minutou","před %n minutami","před %n minutami"], "seconds ago" : "před pár sekundami", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul s ID: %s neexistuje. Povolte ho v nastavení aplikací, nebo kontaktujte vašeho administrátora.", "File name is a reserved word" : "Jméno souboru je rezervované slovo", "File name contains at least one invalid character" : "Jméno souboru obsahuje nejméně jeden neplatný znak", "File name is too long" : "Jméno souboru je moc dlouhé", "Dot files are not allowed" : "Jména souborů začínající tečkou nejsou povolena", "Empty filename is not allowed" : "Prázdné jméno souboru není povoleno", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Aplikace \"%s\" nemůže být nainstalována protože soubor appinfo nelze přečíst.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikaci \"%s\" nelze nainstalovat, protože není kompatibilní s touto verzí serveru.", "This is an automatically sent email, please do not reply." : "Toto je automaticky odesílaný e-mail, prosím, neodpovídejte.", "Help" : "Nápověda", "Apps" : "Aplikace", "Settings" : "Nastavení", "Log out" : "Odhlásit se", "Users" : "Uživatelé", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Základní nastavení", "Sharing" : "Sdílení", "Security" : "Zabezpečení", "Encryption" : "Šifrování", "Additional settings" : "Dodatečná nastavení", "Tips & tricks" : "Tipy a triky", "Personal info" : "Osobní informace", "Sync clients" : "Synchronizační klienti", "Unlimited" : "Neomezeně", "__language_name__" : "Česky", "Verifying" : "Ověření", "Verifying …" : "Ověřování …", "Verify" : "Ověřit", "%s enter the database username and name." : "%s zadejte uživatelské jméno a jméno databáze.", "%s enter the database username." : "Zadejte uživatelské jméno %s databáze.", "%s enter the database name." : "Zadejte název databáze pro %s databáze.", "%s you may not use dots in the database name" : "V názvu databáze %s nesmíte používat tečky.", "Oracle connection could not be established" : "Spojení s Oracle nemohlo být navázáno", "Oracle username and/or password not valid" : "Uživatelské jméno či heslo Oracle není platné", "PostgreSQL username and/or password not valid" : "Uživatelské jméno či heslo PostgreSQL není platné", "You need to enter details of an existing account." : "Musíte zadat údaje existujícího účtu.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X není podporován a %s nebude na této platformě správně fungovat. Používejte pouze na vlastní nebezpečí!", "For the best results, please consider using a GNU/Linux server instead." : "Místo toho zvažte pro nejlepší funkčnost použití GNU/Linux serveru.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Vypadá to, že tato %s instance běží v 32-bitovém PHP prostředí a byl nakonfigurován open_basedir v php.ini. Toto povede k problémům se soubory většími než 4 GB a není doporučováno.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Odstraňte prosím open_basedir nastavení ve svém php.ini nebo přejděte na 64-bitové PHP.", "Set an admin username." : "Zadejte uživatelské jméno správce.", "Set an admin password." : "Zadejte heslo správce.", "Can't create or write into the data directory %s" : "Nelze vytvořit nebo zapisovat do datového adresáře %s", "Invalid Federated Cloud ID" : "Neplatné sdružené cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Sdílení %s selhalo, podpůrná vrstva nepodporuje typ sdílení %i", "Sharing %s failed, because the file does not exist" : "Sdílení %s selhalo, protože soubor neexistuje", "You are not allowed to share %s" : "Nemáte povoleno sdílet %s", "Sharing %s failed, because you can not share with yourself" : "Sdílení %s selhalo, protože nemůžete sdílet sami se sebou", "Sharing %s failed, because the user %s does not exist" : "Sdílení položky %s selhalo, protože uživatel %s neexistuje", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Sdílení položky %s selhalo, protože uživatel %s není členem žádné skupiny společné s uživatelem %s", "Sharing %s failed, because this item is already shared with %s" : "Sdílení položky %s selhalo, protože položka již je s uživatelem %s sdílena", "Sharing %s failed, because this item is already shared with user %s" : "Sdílení položky %s selhalo, protože ta je již s uživatelem %s sdílena", "Sharing %s failed, because the group %s does not exist" : "Sdílení položky %s selhalo, protože skupina %s neexistuje", "Sharing %s failed, because %s is not a member of the group %s" : "Sdílení položky %s selhalo, protože uživatel %s není členem skupiny %s", "You need to provide a password to create a public link, only protected links are allowed" : "Pro vytvoření veřejného odkazu je nutné zadat heslo, jsou povoleny pouze chráněné odkazy", "Sharing %s failed, because sharing with links is not allowed" : "Sdílení položky %s selhalo, protože sdílení pomocí linků není povoleno", "Not allowed to create a federated share with the same user" : "Není povoleno vytvořit propojené sdílení s tím samým uživatelem", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Sdílení %s selhalo, %s se nepodařilo nalézt, server pravděpodobně právě není dostupný.", "Share type %s is not valid for %s" : "Sdílení typu %s není korektní pro %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Nelze nastavit datum vypršení platnosti. Sdílení nemůže vypršet později než za %s po zveřejnění", "Cannot set expiration date. Expiration date is in the past" : "Nelze nastavit datum vypršení platnosti. Datum vypršení je v minulosti", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Úložiště pro sdílení %s musí implementovat rozhraní OCP\\Share_Backend", "Sharing backend %s not found" : "Úložiště sdílení %s nenalezeno", "Sharing backend for %s not found" : "Úložiště sdílení pro %s nenalezeno", "Sharing failed, because the user %s is the original sharer" : "Sdílení položky selhalo, protože uživatel %s je originálním vlastníkem", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Sdílení položky %s selhalo, protože jsou k tomu nutná vyšší oprávnění, než jaká byla %s povolena.", "Sharing %s failed, because resharing is not allowed" : "Sdílení položky %s selhalo, protože znovu-sdílení není povoleno", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Sdílení položky %s selhalo, protože úložiště sdílení %s nenalezla zdroj", "Sharing %s failed, because the file could not be found in the file cache" : "Sdílení položky %s selhalo, protože soubor nebyl nalezen ve vyrovnávací paměti", "Can’t increase permissions of %s" : "Nelze zvýšit oprávnění %s", "Files can’t be shared with delete permissions" : "Soubory nelze sdílet s oprávněními k odstranění", "Files can’t be shared with create permissions" : "Soubory nelze sdílet s oprávněními k vytváření", "Expiration date is in the past" : "Datum vypršení je v minulosti", "Can’t set expiration date more than %s days in the future" : "Nelze nastavit datum vypršení platnosti více než %s dní v budoucnu", "%s shared »%s« with you" : "%s s vámi sdílí »%s«", "%s shared »%s« with you." : "%s s vámi sdílel(a) »%s»", "Click the button below to open it." : "Pro otevření kliknětena tlačítko níže.", "Open »%s«" : "Otevřít »%s«", "%s via %s" : "%s pomocí %s", "The requested share does not exist anymore" : "Požadované sdílení již neexistuje", "Could not find category \"%s\"" : "Nelze nalézt kategorii \"%s\"", "Sunday" : "Neděle", "Monday" : "Pondělí", "Tuesday" : "Úterý", "Wednesday" : "Středa", "Thursday" : "Čtvrtek", "Friday" : "Pátek", "Saturday" : "Sobota", "Sun." : "Ne", "Mon." : "Po", "Tue." : "Út", "Wed." : "St", "Thu." : "Čt", "Fri." : "Pá", "Sat." : "So", "Su" : "Ne", "Mo" : "Po", "Tu" : "Út", "We" : "St", "Th" : "Čt", "Fr" : "Pá", "Sa" : "So", "January" : "Leden", "February" : "Únor", "March" : "Březen", "April" : "Duben", "May" : "Květen", "June" : "Červen", "July" : "Červenec", "August" : "Srpen", "September" : "Září", "October" : "Říjen", "November" : "Listopad", "December" : "Prosinec", "Jan." : "leden", "Feb." : "únor", "Mar." : "březen", "Apr." : "duben", "May." : "květen", "Jun." : "červen", "Jul." : "červenec", "Aug." : "srpen", "Sep." : "září", "Oct." : "říjen", "Nov." : "listopad", "Dec." : "prosinec", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Pouze následující znaky jsou povoleny pro uživatelské jméno: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Musíte zadat platné uživatelské jméno", "Username contains whitespace at the beginning or at the end" : "Uživatelské jméno obsahuje mezery na svém začátku nebo konci", "Username must not consist of dots only" : "Uživatelské jméno se nesmí skládat ze samých teček", "A valid password must be provided" : "Musíte zadat platné heslo", "The username is already being used" : "Uživatelské jméno je již využíváno", "User disabled" : "Uživatel zakázán", "Login canceled by app" : "Přihlášení zrušeno aplikací", "No app name specified" : "Nebyl zadan název aplikace", "App '%s' could not be installed!" : "Aplikaci '%s' nelze nainstalovat!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Aplikaci \"%s\" nelze nainstalovat, protože nejsou splněny následující závislosti: %s", "a safe home for all your data" : "bezpečný domov pro všechna vaše data", "File is currently busy, please try again later" : "Soubor je používán, zkus to později", "Can't read file" : "Nelze přečíst soubor", "Application is not enabled" : "Aplikace není povolena", "Authentication error" : "Chyba ověření", "Token expired. Please reload page." : "Token vypršel. Obnovte prosím stránku.", "Unknown user" : "Neznámý uživatel", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nejsou instalovány ovladače databází (sqlite, mysql nebo postresql).", "Cannot write into \"config\" directory" : "Nelze zapisovat do adresáře \"config\"", "Cannot write into \"apps\" directory" : "Nelze zapisovat do adresáře \"apps\"", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "To lze obvykle vyřešit povolením zápisu webovému serveru do adresáře apps nebo zakázáním appstore v konfiguračním souboru. Viz %s", "Cannot create \"data\" directory" : "Nelze vytvořit datový adresář", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "To lze obvykle vyřešit povolením zápisu webovému serveru do kořenového adresáře. Viz %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Oprávnění lze obvykle napravit povolením zápisu webovému serveru do kořenového adresáře. Viz %s.", "Setting locale to %s failed" : "Nastavení jazyka na %s selhalo", "Please install one of these locales on your system and restart your webserver." : "Prosím nainstalujte alespoň jeden z těchto jazyků do svého systému a restartujte webový server.", "Please ask your server administrator to install the module." : "Požádejte svého správce systému o instalaci tohoto modulu.", "PHP module %s not installed." : "PHP modul %s není nainstalován.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP hodnota \"%s\" není nastavena na \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Změna tohoto nastavení v php.ini umožní Nextcloudu opět běžet", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload je nastaven na \"%s\" místo očekávané hodnoty \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Pro nápravu nastavte <code>mbstring.func_overload</code> na <code>0</code> v souboru php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Je požadováno minimálně libxml2 2.7.0. Aktuálně je nainstalována verze %s.", "To fix this issue update your libxml2 version and restart your web server." : "Pro opravu tohoto problému aktualizujte libxml2 a restartujte web server.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP je patrně nastaveno tak, aby odstraňovalo bloky komentářů. Toto bude mít za následek znepřístupnění mnoha důležitých aplikací.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Toto je pravděpodobně způsobeno aplikacemi pro urychlení načítání jako jsou Zend OPcache nebo eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP moduly jsou nainstalovány, ale stále se tváří jako chybějící?", "Please ask your server administrator to restart the web server." : "Požádejte svého správce systému o restart webového serveru.", "PostgreSQL >= 9 required" : "Je vyžadováno PostgreSQL >= 9", "Please upgrade your database version" : "Aktualizujte prosím verzi své databáze", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Změňte prosím práva na 0770, aby adresář nemohl být otevřen ostatními uživateli.", "Your data directory is readable by other users" : "Váš datový adresář mohou číst ostatní užovatelé", "Your data directory must be an absolute path" : "Váš datový adresář musí být absolutní cesta", "Check the value of \"datadirectory\" in your configuration" : "Ověřte hodnotu \"datadirectory\" ve své konfiguraci", "Your data directory is invalid" : "Váš datový adresář je neplatný", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Ujistěte se, že v kořenovém adresáři je soubor s názvem \".ocdata\".", "Could not obtain lock type %d on \"%s\"." : "Nelze získat zámek typu %d na \"%s\".", "Storage unauthorized. %s" : "Úložiště neověřeno. %s", "Storage incomplete configuration. %s" : "Nekompletní konfigurace úložiště. %s", "Storage connection error. %s" : "Chyba připojení úložiště. %s", "Storage is temporarily not available" : "Úložiště je dočasně nedostupné", "Storage connection timeout. %s" : "Vypršení připojení k úložišti. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "To lze obvykle vyřešit %spovolením zápisu webovému serveru do konfiguračního adresáře%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul s id: %s neexistuje. Povolte ho prosím ve svých nastaveních aplikací nebo kontaktujte svého administrátora.", "Server settings" : "Nastavení serveru", "DB Error: \"%s\"" : "Chyba databáze: \"%s\"", "Offending command was: \"%s\"" : "Příslušný příkaz byl: \"%s\"", "You need to enter either an existing account or the administrator." : "Musíte zadat existující účet či správce.", "Offending command was: \"%s\", name: %s, password: %s" : "Příslušný příkaz byl: \"%s\", jméno: %s, heslo: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Nastavení oprávnění pro %s selhalo, protože jsou k tomu nutná vyšší oprávnění, než jaká byla povolena pro %s", "Setting permissions for %s failed, because the item was not found" : "Nastavení práv pro %s selhalo, protože položka nebyla nalezena", "Cannot clear expiration date. Shares are required to have an expiration date." : "Nelze smazat datum vypršení platnosti. Sdílená data vyžadují datum vypršení platnosti odkazu.", "Cannot increase permissions of %s" : "Nelze navýšit oprávnění u %s", "Files can't be shared with delete permissions" : "Soubory nelze sdílet s oprávněními ke smazání", "Files can't be shared with create permissions" : "Soubory nelze sdílet s vytvořenými oprávněními", "Cannot set expiration date more than %s days in the future" : "Datum vypršení nelze nastavit na více než %s dní do budoucnosti", "Personal" : "Osobní", "Admin" : "Administrace", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "To lze obvykle vyřešit %spovolením zápisu webovému serveru do adresáře apps%s nebo zakázáním appstore v konfiguračním souboru.", "Cannot create \"data\" directory (%s)" : "Nelze vytvořit adresář \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Toto může být obvykle opraveno <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">nastavením přístupových práv webového serveru pro zápis do kořenového adresáře</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Oprávnění lze obvykle napravit %spovolením zápisu webovému serveru do kořenového adresáře%s.", "Data directory (%s) is readable by other users" : "Datový adresář (%s) je čitelný i ostatními uživateli", "Data directory (%s) must be an absolute path" : "Cesta k datovému adresáři (%s) musí být uvedena absolutně", "Data directory (%s) is invalid" : "Datový adresář (%s) je neplatný", "Please check that the data directory contains a file \".ocdata\" in its root." : "Ověřte prosím, že kořenový adresář s daty obsahuje soubor \".ocdata\"." }, "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"); l10n/de.js 0000604 00000057303 15247130447 0006257 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Das Schreiben in das „config“-Verzeichnis ist nicht möglich!", "This can usually be fixed by giving the webserver write access to the config directory" : "Dies kann normalerweise repariert werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird", "See %s" : "Siehe %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Die Dateien der App %$1s wurden nicht korrekt ersetzt. Stelle sicher, dass die Version mit dem Server kompatibel ist.", "Sample configuration detected" : "Beispielkonfiguration gefunden", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Es wurde festgestellt, dass die Beispielkonfiguration kopiert wurde. Dies kann Deine Installation zerstören und wird nicht unterstützt. Bitte die Dokumentation lesen, bevor Änderungen an der config.php vorgenommen werden.", "%1$s and %2$s" : "%1$s und %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s und %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s und %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s und %5$s", "Education Edition" : "Bildungsausgabe", "Enterprise bundle" : "Firmen-Paket", "Groupware bundle" : "Groupware-Paket", "Social sharing bundle" : "Paket für das Teilen in sozialen Medien", "PHP %s or higher is required." : "PHP %s oder höher wird benötigt.", "PHP with a version lower than %s is required." : "PHP wird in einer früheren Version als %s benötigt.", "%sbit or higher PHP required." : "%sbit oder höheres PHP wird benötigt.", "Following databases are supported: %s" : "Die folgenden Datenbanken werden unterstützt: %s", "The command line tool %s could not be found" : "Das Kommandozeilenwerkzeug %s konnte nicht gefunden werden", "The library %s is not available." : "Die Bibliothek %s ist nicht verfügbar.", "Library %s with a version higher than %s is required - available version %s." : "Die Bibliothek %s wird in einer neueren Version als %s benötigt - verfügbare Version ist %s.", "Library %s with a version lower than %s is required - available version %s." : "Die Bibliothek %s wird in einer früheren Version als %s benötigt - verfügbare Version ist %s.", "Following platforms are supported: %s" : "Die folgenden Plattformen werden unterstützt: %s", "Server version %s or higher is required." : "Server Version %s oder höher wird benötigt.", "Server version %s or lower is required." : "Server Version %s oder niedriger wird benötigt.", "Unknown filetype" : "Unbekannter Dateityp", "Invalid image" : "Ungültiges Bild", "Avatar image is not square" : "Benutzerbild ist nicht quadratisch", "today" : "Heute", "yesterday" : "Gestern", "_%n day ago_::_%n days ago_" : ["Vor %n Tag","Vor %n Tagen"], "last month" : "Letzten Monat", "_%n month ago_::_%n months ago_" : ["Vor %n Monat","Vor %n Monaten"], "last year" : "Letztes Jahr", "_%n year ago_::_%n years ago_" : ["Vor %n Jahr","Vor %n Jahren"], "_%n hour ago_::_%n hours ago_" : ["Vor %n Stunde","Vor %n Stunden"], "_%n minute ago_::_%n minutes ago_" : ["Vor %n Minute","Vor %n Minuten"], "seconds ago" : "Gerade eben", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte die App in den App-Einstellungen aktivieren oder den Administrator kontaktieren.", "File name is a reserved word" : "Der Dateiname ist ein reserviertes Wort", "File name contains at least one invalid character" : "Der Dateiname enthält mindestens ein ungültiges Zeichen", "File name is too long" : "Dateiname ist zu lang", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", "Empty filename is not allowed" : "Ein leerer Dateiname ist nicht erlaubt", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Die Anwendung \"%s\" kann nicht installiert werden, weil die Anwendungsinfodatei nicht gelesen werden kann.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Die App \"%s\" kann nicht installiert werden, da sie mit dieser Serverversion nicht kompatibel ist.", "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.", "Help" : "Hilfe", "Apps" : "Apps", "Settings" : "Einstellungen", "Log out" : "Abmelden", "Users" : "Benutzer", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Grundeinstellungen", "Sharing" : "Teilen", "Security" : "Sicherheit", "Encryption" : "Verschlüsselung", "Additional settings" : "Zusätzliche Einstellungen", "Tips & tricks" : "Tipps & Tricks", "Personal info" : "Persönliche Informationen ", "Sync clients" : " Sync-Clients ", "Unlimited" : "Unbegrenzt", "__language_name__" : " Deutsch (Persönlich: Du) ", "Verifying" : "Überprüfe", "Verifying …" : " Überprüfe… ", "Verify" : "Überprüfen", "%s enter the database username and name." : "%s gebe den Datenbank-Benutzernamen und den Datenbanknamen ein.", "%s enter the database username." : "%s gebe den Datenbank-Benutzernamen an.", "%s enter the database name." : "%s gebe den Datenbanknamen an.", "%s you may not use dots in the database name" : "%s Der Datenbankname darf keine Punkte enthalten", "Oracle connection could not be established" : "Es konnte keine Verbindung zur Oracle-Datenbank hergestellt werden", "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "You need to enter details of an existing account." : "Du musst Details von einem existierenden Benutzer einfügen.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", "For the best results, please consider using a GNU/Linux server instead." : "Zur Gewährleistung eines optimalen Betriebs sollte stattdessen ein GNU/Linux-Server verwendet werden.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Es scheint, dass diese %s-Instanz unter einer 32-Bit-PHP-Umgebung läuft und open_basedir in der Datei php.ini konfiguriert worden ist. Von einem solchen Betrieb wird dringend abgeraten, weil es dabei zu Problemen mit Dateien kommt, deren Größe 4 GB übersteigt.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Bitte entferne die open_basedir-Einstellung in Deiner php.ini oder wechsele zu 64-Bit-PHP.", "Set an admin username." : "Einen Administrator-Benutzernamen setzen.", "Set an admin password." : "Ein Administrator-Passwort setzen.", "Can't create or write into the data directory %s" : "Das Datenverzeichnis %s kann nicht erstellt oder es kann darin nicht geschrieben werden.", "Invalid Federated Cloud ID" : "Ungültige Federated-Cloud-ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Freigabe von %s fehlgeschlagen, da das Backend die Freigabe vom Typ %i nicht erlaubt.", "Sharing %s failed, because the file does not exist" : "Freigabe von %s fehlgeschlagen, da die Datei nicht existiert", "You are not allowed to share %s" : "Die Freigabe von %s ist Dir nicht erlaubt", "Sharing %s failed, because you can not share with yourself" : "Freigabe von %s fehlgeschlagen, da du nichts mit dir selbst teilen kannst", "Sharing %s failed, because the user %s does not exist" : "Freigabe von %s fehlgeschlagen, da der Benutzer %s nicht existiert", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Freigabe von %s fehlgeschlagen, da der Benutzer %s kein Gruppenmitglied einer der Gruppen von %s ist", "Sharing %s failed, because this item is already shared with %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit %s geteilt wird", "Sharing %s failed, because this item is already shared with user %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit dem Benutzer %s geteilt wird", "Sharing %s failed, because the group %s does not exist" : "Freigabe von %s fehlgeschlagen, da die Gruppe %s nicht existiert", "Sharing %s failed, because %s is not a member of the group %s" : "Freigabe von %s fehlgeschlagen, da %s kein Mitglied der Gruppe %s ist", "You need to provide a password to create a public link, only protected links are allowed" : "Es sind nur geschützte Links zulässig, daher musst Du ein Passwort angeben, um einen öffentlichen Link zu generieren", "Sharing %s failed, because sharing with links is not allowed" : "Freigabe von %s fehlgeschlagen, da das Teilen von Verknüpfungen nicht erlaubt ist", "Not allowed to create a federated share with the same user" : "Das Erstellen einer Federated-Cloud-Freigabe mit dem gleichen Benutzer ist nicht erlaubt", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Freigabe von %s fehlgeschlagen, da %s nicht gefunden wurde. Möglicherweise ist der Server nicht erreichbar.", "Share type %s is not valid for %s" : "Freigabetyp %s ist nicht gültig für %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Ablaufdatum kann nicht gesetzt werden. Freigaben können nach dem Teilen, nicht länger als %s gültig sein.", "Cannot set expiration date. Expiration date is in the past" : "Ablaufdatum kann nicht gesetzt werden. Ablaufdatum liegt in der Vergangenheit.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Freigabe-Backend %s muss in der OCP\\Share_Backend - Schnittstelle implementiert werden", "Sharing backend %s not found" : "Freigabe-Backend %s nicht gefunden", "Sharing backend for %s not found" : "Freigabe-Backend für %s nicht gefunden", "Sharing failed, because the user %s is the original sharer" : "Freigabe fehlgeschlagen, da der Benutzer %s der ursprünglich Teilende ist", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Freigabe von %s fehlgeschlagen, da die Berechtigungen die erteilten Berechtigungen %s überschreiten", "Sharing %s failed, because resharing is not allowed" : "Freigabe von %s fehlgeschlagen, da das nochmalige Freigeben einer Freigabe nicht erlaubt ist", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Freigabe von %s fehlgeschlagen, da das Freigabe-Backend für %s nicht in dieser Quelle gefunden werden konnte", "Sharing %s failed, because the file could not be found in the file cache" : "Freigabe von %s fehlgeschlagen, da die Datei im Datei-Cache nicht gefunden werden konnte", "Can’t increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen", "Files can’t be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden", "Files can’t be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden", "Expiration date is in the past" : "Das Ablaufdatum liegt in der Vergangenheit.", "Can’t set expiration date more than %s days in the future" : "Das Ablaufdatum kann nicht mehr als %s Tage in die Zukunft liegen", "%s shared »%s« with you" : "%s hat „%s“ mit Dir geteilt", "%s shared »%s« with you." : "%s hat mit Dir »%s« geteilt.", "Click the button below to open it." : "Klicke zum Öffnen auf die untere Schaltfläche.", "Open »%s«" : "»%s« öffnen", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "Die angeforderte Freigabe existiert nicht mehr", "Could not find category \"%s\"" : "Die Kategorie \"%s“ konnte nicht gefunden werden", "Sunday" : "Sonntag", "Monday" : "Montag", "Tuesday" : "Dienstag", "Wednesday" : "Mittwoch", "Thursday" : "Donnerstag", "Friday" : "Freitag", "Saturday" : "Samstag", "Sun." : "Son.", "Mon." : "Mon.", "Tue." : "Die.", "Wed." : "Mit.", "Thu." : "Don.", "Fri." : "Fre.", "Sat." : "Sam.", "Su" : "So", "Mo" : "Mo", "Tu" : "Di", "We" : "Mi", "Th" : "Do", "Fr" : "Fr", "Sa" : "Sa", "January" : "Januar", "February" : "Februar", "March" : "März", "April" : "April", "May" : "Mai", "June" : "Juni", "July" : "Juli", "August" : "August", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "Dezember", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mär.", "Apr." : "Apr.", "May." : "Mai", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dez.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Folgende Zeichen sind im Benutzernamen erlaubt: „a-z“, „A-Z“, „0-9“ und „_.@-'“", "A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden", "Username contains whitespace at the beginning or at the end" : "Der Benutzername enthält Leerzeichen am Anfang oder am Ende", "Username must not consist of dots only" : "Der Benutzername darf nicht nur aus Punkten bestehen", "A valid password must be provided" : "Es muss ein gültiges Passwort eingegeben werden", "The username is already being used" : "Dieser Benutzername existiert bereits", "Could not create user" : "Benutzer konnte nicht erstellt werden", "User disabled" : "Nutzer deaktiviert", "Login canceled by app" : "Anmeldung durch die App abgebrochen", "No app name specified" : "Es wurde kein App-Name angegeben", "App '%s' could not be installed!" : "'%s' - App konnte nicht installiert werden!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Die App „%s“ kann nicht installiert werden, da die folgenden Abhängigkeiten nicht erfüllt sind: %s", "a safe home for all your data" : "ein sicherer Ort für all Deine Daten", "File is currently busy, please try again later" : "Die Datei ist in Benutzung, bitte versuche es später noch einmal", "Can't read file" : "Datei kann nicht gelesen werden", "Application is not enabled" : "Die Anwendung ist nicht aktiviert", "Authentication error" : "Authentifizierungsfehler", "Token expired. Please reload page." : "Token abgelaufen. Bitte lade die Seite neu.", "Unknown user" : "Unbekannter Benutzer", "No database drivers (sqlite, mysql, or postgresql) installed." : "Keine Datenbanktreiber (SQLite, MySQL oder PostgreSQL) installiert.", "Cannot write into \"config\" directory" : "Schreiben in das „config“-Verzeichnis ist nicht möglich", "Cannot write into \"apps\" directory" : "Schreiben in das „apps“-Verzeichnis ist nicht möglich", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das App-Verzeichnis eingeräumt wird. Siehe auch %s", "Cannot create \"data\" directory" : "Kann das \"Daten\"-Verzeichnis nicht erstellen", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Berechtigungen können zumeist korrigiert werden indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s.", "Setting locale to %s failed" : "Das Setzen der Umgebungslokale auf %s ist fehlgeschlagen", "Please install one of these locales on your system and restart your webserver." : "Bitte installiere eine dieser Sprachen auf Deinem System und starte den Webserver neu.", "Please ask your server administrator to install the module." : "Bitte für die Installation des Moduls Deinen Server-Administrator kontaktieren.", "PHP module %s not installed." : "PHP-Modul %s nicht installiert.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP-Einstellung „%s“ ist nicht auf „%s“ gesetzt.", "Adjusting this setting in php.ini will make Nextcloud run again" : "Eine Änderung dieser Einstellung in der php.ini kann deine Nextcloud wieder lauffähig machen.", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload ist nicht auf den erwarteten Wert „0“, sondern stattdessen auf „%s“ gesetzt", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in Deiner php.ini auf <code>0</code>.", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 ist mindestestens erforderlich. Im Moment ist %s installiert.", "To fix this issue update your libxml2 version and restart your web server." : "Um den Fehler zu beheben, musst Du die libxml2 Version aktualisieren und den Webserver neustarten.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ist offenbar so konfiguriert, dass PHPDoc-Blöcke in der Anweisung entfernt werden. Dadurch sind mehrere Kern-Apps nicht erreichbar.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dies wird wahrscheinlich durch Zwischenspeicher/Beschleuniger wie etwa Zend OPcache oder eAccelerator verursacht.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-Module wurden installiert, werden aber als noch fehlend gelistet?", "Please ask your server administrator to restart the web server." : "Bitte kontaktiere Deinen Server-Administrator und bitte um den Neustart des Webservers.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 benötigt", "Please upgrade your database version" : "Bitte aktualisiere deine Datenbankversion", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Bitte ändere die Berechtigungen auf 0770, sodass das Verzeichnis nicht von anderen Benutzern angezeigt werden kann.", "Your data directory is readable by other users" : "Dein Datenverzeichnis kann von anderen Benutzern gelesen werden", "Your data directory must be an absolute path" : "Dein Datenverzeichnis muss einen eindeutigen Pfad haben", "Check the value of \"datadirectory\" in your configuration" : "Überprüfe bitte die Angabe unter „datadirectory“ in Deiner Konfiguration", "Your data directory is invalid" : "Dein Datenverzeichnis ist ungültig", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Stelle sicher, dass eine Datei \".ocdata\" im Wurzelverzeichnis des data-Verzeichnisses existiert.", "Could not obtain lock type %d on \"%s\"." : "Sperrtyp %d auf „%s“ konnte nicht ermittelt werden.", "Storage unauthorized. %s" : "Speicher nicht authorisiert. %s", "Storage incomplete configuration. %s" : "Speicher-Konfiguration unvollständig. %s", "Storage connection error. %s" : "Verbindungsfehler zum Speicherplatz. %s", "Storage is temporarily not available" : "Speicher ist vorübergehend nicht verfügbar", "Storage connection timeout. %s" : "Zeitüberschreitung der Verbindung zum Speicherplatz. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Dies kann normalerweise behoben werden, %sindem dem Webserver Schreibzugriff auf das Konfigurationsverzeichnis gegeben wird %s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte die Aktivierung in Deinen App-Einstellungen vornehmen oder Deine Administrator kontaktieren.", "Server settings" : "Servereinstellungen", "DB Error: \"%s\"" : "DB-Fehler: „%s“", "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", "You need to enter either an existing account or the administrator." : "Du musst entweder ein existierendes Benutzerkonto oder das Administratorenkonto angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Das Setzen der Berechtigungen für %s ist fehlgeschlagen, da die neuen Berechtigungen, die erteilten Berechtigungen %s überschreiten", "Setting permissions for %s failed, because the item was not found" : "Das Setzen der Berechtigungen für %s ist fehlgeschlagen, da das Element nicht gefunden wurde", "Cannot clear expiration date. Shares are required to have an expiration date." : "Ablaufdatum kann nicht gelöscht werden. Freigaben werden für ein Ablaufdatum benötigt.", "Cannot increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen", "Files can't be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden", "Files can't be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden", "Cannot set expiration date more than %s days in the future" : "Das Ablaufdatum kann nicht mehr als %s Tage in die Zukunft liegen", "Personal" : "Persönlich", "Admin" : "Verwaltung", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Dies kann normalerweise behoben werden, %sindem dem Webserver Schreibzugriff auf das App-Verzeichnis gegeben wird%s oder der App Store in der Konfigurationsdatei deaktiviert wird.", "Cannot create \"data\" directory (%s)" : "Erstellen des „data“-Verzeichnisses ist nicht möglich (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Dies kann normalerweise repariert werden, indem dem Webserver <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> Schreibzugriff auf das Wurzelverzeichnis gegeben wird</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Berechtigungen können normalerweise repariert werden, indem dem Webserver %s Schreibzugriff auf das Wurzelverzeichnis %s gegeben wird.", "Data directory (%s) is readable by other users" : "Datenverzeichnis (%s) ist von anderen Nutzern lesbar", "Data directory (%s) must be an absolute path" : "Das Datenverzeichnis (%s) muss ein absoluter Pfad sein", "Data directory (%s) is invalid" : "Datenverzeichnis (%s) ist ungültig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Bitte stelle sicher, dass das Datenverzeichnis auf seiner ersten Ebene eine Datei namens „.ocdata“ enthält." }, "nplurals=2; plural=(n != 1);"); l10n/sk.js 0000604 00000045460 15247130447 0006305 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Nie je možné zapisovat do priečinka \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory" : "To je zvyčajne možné opraviť tým, že udelíte webovému serveru oprávnenie na zápis do priečinka s konfiguráciou.", "See %s" : "Pozri %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Súbory aplikácie %$1s nebolo možné úspešne nahradiť. Uistite sa, že verzia je kompatibilná s verziou servera.", "Sample configuration detected" : "Detekovaná bola vzorová konfigurácia", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Zistilo sa, že konfigurácia bola skopírovaná zo vzorových súborov. Takáto konfigurácia nie je podporovaná a môže poškodiť vašu inštaláciu. Prečítajte si dokumentáciu pred vykonaním zmien v config.php", "%1$s and %2$s" : "%1$s a %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s a %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s a %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s a %5$s", "PHP %s or higher is required." : "Požadovaná verzia PHP %s alebo vyššia.", "PHP with a version lower than %s is required." : "PHP je vyžadované vo vyššej verzii ako %s.", "%sbit or higher PHP required." : "%sbit alebo vyššie PHP je vyžadované.", "Following databases are supported: %s" : "Podporované sú tieto databázy: %s", "The command line tool %s could not be found" : "Nástroj príkazového riadka %s nebol nájdený", "The library %s is not available." : "Knižnica %s je nedostupná.", "Library %s with a version higher than %s is required - available version %s." : "Požadovaná je knižnica %s vo vyššej verzii ako %s - dostupná verzia %s.", "Library %s with a version lower than %s is required - available version %s." : "Požadovaná je knižnica %s v nižšej verzii ako %s - dostupná verzia %s.", "Following platforms are supported: %s" : "Podporované sú nasledovné systémy: %s", "Server version %s or higher is required." : "Je vyžadovaná verzia servera %s alebo vyššia.", "Server version %s or lower is required." : "Je vyžadovaná verzia servera %s alebo nižšia.", "Unknown filetype" : "Neznámy typ súboru", "Invalid image" : "Chybný obrázok", "Avatar image is not square" : "Obrázok avatara nie je štvorcový", "today" : "dnes", "yesterday" : "včera", "_%n day ago_::_%n days ago_" : ["včera","pred %n dňami","pred %n dňami"], "last month" : "minulý mesiac", "_%n month ago_::_%n months ago_" : ["pred %n mesiacom","pred %n mesiacmi","pred %n mesiacmi"], "last year" : "minulý rok", "_%n year ago_::_%n years ago_" : ["vlani","pred %n rokmi","pred %n rokmi"], "_%n hour ago_::_%n hours ago_" : ["pred %n hodinou","pred %n hodinami","pred %n hodinami"], "_%n minute ago_::_%n minutes ago_" : ["pred %n minútou","pred %n minútami","pred %n minútami"], "seconds ago" : "pred sekundami", "File name is a reserved word" : "Názov súboru je rezervované slovo.", "File name contains at least one invalid character" : "Názov súboru obsahuje nepovolené znaky.", "File name is too long" : "Meno súboru je veľmi dlhé.", "Dot files are not allowed" : "Názov súboru začínajúci bodkou nie je povolený.", "Empty filename is not allowed" : "Prázdny názov súboru nie je povolený", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Aplikáciu \"%s\" nie je možné nainštalovať, lebo nebolo možné načítať súbor s informáciami o aplikácií.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikácia \"%s\" nie je kompatibilná s verziou servera, preto nemôže byť nainštalovaná.", "Help" : "Pomoc", "Apps" : "Aplikácie", "Users" : "Používatelia", "APCu" : "APCu", "Redis" : "Redis", "Sharing" : "Sprístupnenie", "Encryption" : "Šifrovanie", "Additional settings" : "Ďalšie nastavenia", "Tips & tricks" : "Tipy a triky", "%s enter the database username." : "Zadajte používateľské meno %s databázy.", "%s enter the database name." : "Zadajte názov databázy pre %s databázy.", "%s you may not use dots in the database name" : "V názve databázy %s nemôžete používať bodky", "Oracle connection could not be established" : "Nie je možné pripojiť sa k Oracle", "Oracle username and/or password not valid" : "Používateľské meno a/alebo heslo pre Oracle databázu je neplatné", "PostgreSQL username and/or password not valid" : "Používateľské meno a/alebo heslo pre PostgreSQL databázu je neplatné", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie je podporovaný a %s nebude správne fungovať na tejto platforme. Použite ho na vlastné riziko!", "For the best results, please consider using a GNU/Linux server instead." : "Pre dosiahnutie najlepších výsledkov, prosím zvážte použitie GNU/Linux servera.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Zdá sa, že táto inštancia %s beží v 32-bitovom prostredí PHP a v php.ini bola nastavená voľba open_basedir. To bude zdrojom problémov so súbormi väčšími ako 4GB a dôrazne sa neodporúča.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Prosím, odstráňte nastavenie open_basedir vo vašom php.ini alebo prejdite na 64-bit PHP.", "Set an admin username." : "Zadajte používateľské meno administrátora.", "Set an admin password." : "Zadajte heslo administrátora.", "Can't create or write into the data directory %s" : "Nemožno vytvoriť alebo zapisovať do priečinka dát %s", "Invalid Federated Cloud ID" : "Neplatné združené Cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Sprístupnenie %s zlyhalo, backend nepodporuje typ sprístupnenia %i", "Sharing %s failed, because the file does not exist" : "Nie je možné sprístupniť %s, súbor neexistuje", "You are not allowed to share %s" : "Nemôžete sprístupniť %s", "Sharing %s failed, because you can not share with yourself" : "Sprístupnenie %s zlyhalo, nieje možné sprístupniť obsah so sebou samým", "Sharing %s failed, because the user %s does not exist" : "Sprístupnenie %s zlyhalo, používateľ %s neexistuje", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Sprístupnenie %s zlyhalo, používateľ %s nie je členom žiadnej skupiny spoločnej s používateľom %s", "Sharing %s failed, because this item is already shared with %s" : "Sprístupnenie %s zlyhalo, pretože táto položka už je prístupná pre %s", "Sharing %s failed, because this item is already shared with user %s" : "Sprístupnenie %s zlyhalo, táto položka už je používateľovi %s prístupná", "Sharing %s failed, because the group %s does not exist" : "Sprístupnenie %s zlyhalo, skupina %s neexistuje", "Sharing %s failed, because %s is not a member of the group %s" : "Sprístupnenie %s zlyhalo, %s nie je členom skupiny %s", "You need to provide a password to create a public link, only protected links are allowed" : "Musíte zadať heslo ak chcete vytvoriť verejný odkaz, lebo iba odkazy chránené heslom sú povolené", "Sharing %s failed, because sharing with links is not allowed" : "%s nie je možné sprístupniť, sprístupnenie prostredníctvom odkazu nie je povolené", "Not allowed to create a federated share with the same user" : "Nie je možné vytvoriť združené sprístupnenie so sebou samým", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Sprístupňovanie %s zlyhalo, nepodarilo sa nájsť %s, možno je server dočasne nedostupný.", "Share type %s is not valid for %s" : "Typ sprístupnenia %s nie je možný pre %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Sprístupnenie nemôže byť ukončené skôr, ako po %s dňoch.", "Cannot set expiration date. Expiration date is in the past" : "Nie je možné nastaviť dátum konca platnosti. Dátum konca platnosti je v minulosti.", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Backend pre sprístupnenie %s musí implementovať rozhranie OCP\\Share_Backend", "Sharing backend %s not found" : "Backend sprístupnenia %s nebol nájdený", "Sharing backend for %s not found" : "Backend sprístupnenia pre %s nebol nájdený", "Sharing failed, because the user %s is the original sharer" : "Sprístupnenie zlyhalo, pretože používateľ %s je pôvodný spoločný používateľ", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Sprístupnenie %s zlyhalo, pretože povolenia prekračujú povolenia udelené %s", "Sharing %s failed, because resharing is not allowed" : "Nie je možné sprístupniť %s ďalším osobám", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Sprístupnenie %s zlyhalo, backend nenašiel zdrojový %s", "Sharing %s failed, because the file could not be found in the file cache" : "Sprístupnenie %s zlyhalo, pretože súbor sa nenachádza vo vyrovnávacej pamäti súborov", "Expiration date is in the past" : "Dátum konca platnosti je v minulosti", "%s shared »%s« with you" : "%s vám sprístupnil »%s«", "%s via %s" : "%s cez %s", "Could not find category \"%s\"" : "Nemožno nájsť danú kategóriu \"%s\"", "Sunday" : "Nedeľa", "Monday" : "Pondelok", "Tuesday" : "Utorok", "Wednesday" : "Streda", "Thursday" : "Štvrtok", "Friday" : "Piatok", "Saturday" : "Sobota", "Sun." : "Ned.", "Mon." : "Pon.", "Tue." : "Uto.", "Wed." : "Str.", "Thu." : "Štv.", "Fri." : "Pia.", "Sat." : "Sob.", "Su" : "Ne", "Mo" : "Po", "Tu" : "Ut", "We" : "St", "Th" : "Št", "Fr" : "Pi", "Sa" : "So", "January" : "Január", "February" : "Február", "March" : "Marec", "April" : "Apríl", "May" : "Máj", "June" : "Jún", "July" : "Júl", "August" : "August", "September" : "September", "October" : "Október", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mar.", "Apr." : "Apr.", "May." : "Máj.", "Jun." : "Jún.", "Jul." : "Júl.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "V mene používateľa je možné použiť iba nasledovné znaky: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Musíte zadať platné používateľské meno", "Username contains whitespace at the beginning or at the end" : "Meno používateľa obsahuje na začiatku, alebo na konci medzeru", "A valid password must be provided" : "Musíte zadať platné heslo", "The username is already being used" : "Meno používateľa je už použité", "User disabled" : "Používateľ zakázaný", "Login canceled by app" : "Prihlásenie bolo zrušené aplikáciou", "No app name specified" : "Nešpecifikované meno aplikácie", "App '%s' could not be installed!" : "Aplikáciu '%s' nebolo možné nainštalovať!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "Aplikáciu \"%s\" nie je možné inštalovať, pretože nie sú splnené nasledovné závislosti: %s", "a safe home for all your data" : "bezpečný domov pre všetky vaše dáta", "File is currently busy, please try again later" : "Súbor sa práve používa, skúste prosím neskôr", "Can't read file" : "Nemožno čítať súbor.", "Application is not enabled" : "Aplikácia nie je zapnutá", "Authentication error" : "Chyba autentifikácie", "Token expired. Please reload page." : "Token vypršal. Obnovte, prosím, stránku.", "Unknown user" : "Neznámy používateľ", "No database drivers (sqlite, mysql, or postgresql) installed." : "Ovládače databázy (sqlite, mysql, alebo postgresql) nie sú nainštalované.", "Cannot write into \"config\" directory" : "Nie je možné zapisovať do priečinka \"config\"", "Cannot write into \"apps\" directory" : "Nie je možné zapisovať do priečinka \"apps\"", "Setting locale to %s failed" : "Nastavenie locale na %s zlyhalo", "Please install one of these locales on your system and restart your webserver." : "Prosím, nainštalujte si aspoň jeden z týchto jazykov so svojho systému a reštartujte webserver.", "Please ask your server administrator to install the module." : "Prosím, požiadajte administrátora vášho servera o inštaláciu modulu.", "PHP module %s not installed." : "PHP modul %s nie je nainštalovaný.", "PHP setting \"%s\" is not set to \"%s\"." : "Voľba PHP „%s“ nie je nastavená na „%s“.", "Adjusting this setting in php.ini will make Nextcloud run again" : "Použitím týchto nastavení v php.ini dovolí Nextcloudu sa znova spustiť", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload je nastavený na \"%s\", namiesto predpokladanej hodnoty \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Oprava problému spočíva v nastavení <code>mbstring.func_overload</code> na <code>0</code> vo vašom php.ini", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Vyžadovaná verzia libxml2 je 2.7.0 a vyššia. Momentálne je nainštalovaná verzia %s.", "To fix this issue update your libxml2 version and restart your web server." : "Pre vyriešenie tohto problému aktualizujte prosím verziu libxml2 a reštartujte webový server.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP je zjavne nastavené, aby odstraňovalo bloky vloženej dokumentácie. To zneprístupní niekoľko základných aplikácií.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "To je pravdepodobne spôsobené cache/akcelerátorom ako napr. Zend OPcache alebo eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP moduly boli nainštalované, ale stále sa tvária, že chýbajú?", "Please ask your server administrator to restart the web server." : "Prosím, požiadajte administrátora vášho servera o reštartovanie webového servera.", "PostgreSQL >= 9 required" : "Vyžadované PostgreSQL >= 9", "Please upgrade your database version" : "Prosím, aktualizujte verziu svojej databázy", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Prosím, zmeňte oprávnenia na 0770, aby tento priečinok nemohli ostatní používatelia otvoriť.", "Check the value of \"datadirectory\" in your configuration" : "Skontrolujte hodnotu \"datadirectory\" vo vašej konfigurácii", "Could not obtain lock type %d on \"%s\"." : "Nepodarilo sa získať zámok typu %d na „%s“.", "Storage connection error. %s" : "Chyba pripojenia k úložisku. %s", "Storage is temporarily not available" : "Úložisko je dočasne nedostupné", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "To je zvyčajne možné opraviť tým, že %s udelíte webovému serveru oprávnenie na zápis k adresáru s konfiguráciou%s.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul s ID: %s neexistuje. Povoľte ho prosím vo vašom nastavení aplikácií alebo konaktujte správcu.", "Server settings" : "Nastavenia servera", "DB Error: \"%s\"" : "Chyba DB: \"%s\"", "Offending command was: \"%s\"" : "Podozrivý príkaz bol: \"%s\"", "You need to enter either an existing account or the administrator." : "Musíte zadať jestvujúci účet alebo administrátora.", "Offending command was: \"%s\", name: %s, password: %s" : "Podozrivý príkaz bol: \"%s\", meno: %s, heslo: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Nastavenie povolení pre %s zlyhalo, pretože povolenia prekračujú povolenia udelené %s", "Setting permissions for %s failed, because the item was not found" : "Nastavenie povolení pre %s zlyhalo, pretože položka sa nenašla", "Cannot clear expiration date. Shares are required to have an expiration date." : "Nemožno vymazať čas expirácie. Pri sprístupnení je čas exspirácie vyžadovaný.", "Cannot set expiration date more than %s days in the future" : "Nie je možné nastaviť dátum konca platnosti viac ako %s dní v budúcnosti", "Personal" : "Osobné", "Admin" : "Administrátor", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Toto je zvyčajne možné opraviť tým, že %s udelíte webovému serveru oprávnenie na zápis do priečinka aplikácií %s alebo vypnete obchod s aplikáciami v konfiguračnom súbore.", "Cannot create \"data\" directory (%s)" : "Nie je možné vytvoriť priečinok \"data\" (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "To je zvyčajne možné opraviť tým <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">že udelíte webovému serveru oprávnenie na zápis do koreňového priečinka</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Oprávnenia je zvyčajne možné opraviť tým, že %sudelíte webovému serveru oprávnenie na zápis do koreňového priečinka%s.", "Data directory (%s) is readable by other users" : "Priečinok dát (%s) je prístupný na čítanie ostatným používateľom", "Data directory (%s) must be an absolute path" : "Priečinok dát (%s) musí byť zadaný ako absolútna cesta", "Data directory (%s) is invalid" : "Priečinok dát (%s) je neplatný", "Please check that the data directory contains a file \".ocdata\" in its root." : "Prosím, skontrolujte, či priečinok dát obsahuje súbor \".ocdata\"." }, "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"); l10n/zh_TW.js 0000604 00000047203 15247130447 0006720 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "無法寫入 \"config\" 目錄!", "This can usually be fixed by giving the webserver write access to the config directory" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題", "See %s" : "見 %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題,詳見 %s", "Sample configuration detected" : "偵測到範本設定", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "看來您直接複製了範本設定來使用,這可能會毀掉你的安裝,請閱讀說明文件後對 config.php 進行適當的修改", "PHP %s or higher is required." : "需要 PHP %s 或更高版本", "PHP with a version lower than %s is required." : "需要 PHP 版本低於 %s ", "%sbit or higher PHP required." : "%s 或需要更高階版本的php", "Following databases are supported: %s" : "支援下列資料庫: %s", "The command line tool %s could not be found" : "找不到命令列工具指令 %s", "The library %s is not available." : "套件庫 %s 無法使用", "Library %s with a version higher than %s is required - available version %s." : "需要套件庫 %s 版本高於 %s - 可使用的版本是 %s", "Library %s with a version lower than %s is required - available version %s." : "需要套件庫 %s 版本低於 %s - 可使用的版本是 %s", "Following platforms are supported: %s" : "支援下列平台: %s", "Server version %s or higher is required." : "需要伺服器版本 %s 或更高", "Server version %s or lower is required." : "需要伺服器版本 %s 或更低", "Unknown filetype" : "未知的檔案類型", "Invalid image" : "無效的圖片", "Avatar image is not square" : "頭像不是正方形", "today" : "今天", "yesterday" : "昨天", "_%n day ago_::_%n days ago_" : ["%n 天前"], "last month" : "上個月", "_%n month ago_::_%n months ago_" : ["%n 個月前"], "last year" : "去年", "_%n year ago_::_%n years ago_" : ["%n 幾年前"], "_%n hour ago_::_%n hours ago_" : ["%n 小時前"], "_%n minute ago_::_%n minutes ago_" : ["%n 分鐘前"], "seconds ago" : "幾秒前", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "名為 %s 的模組不存在,請在應用程式設定中啟用,或是聯絡系統管理員", "File name is a reserved word" : "檔案名稱是保留字", "File name contains at least one invalid character" : "檔案名稱含有不允許的字元", "File name is too long" : "檔案名稱太長", "Dot files are not allowed" : "不允許小數點開頭的檔案", "Empty filename is not allowed" : "不允許空白的檔名", "App \"%s\" cannot be installed because appinfo file cannot be read." : "應用程式 \"%s\" 無法安裝,因為無法讀取 appinfo 檔案。", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "應用程式 \"%s\" 無法安裝,因為該應用程式不相容於目前版本的伺服器。", "This is an automatically sent email, please do not reply." : "此為自動寄送的電子郵件,請不要回覆。", "Help" : "說明", "Apps" : "應用程式", "Settings" : "設定", "Log out" : "登出", "Users" : "使用者", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "基本設定", "Sharing" : "分享", "Security" : "安全性", "Encryption" : "加密", "Additional settings" : "其他設定", "Tips & tricks" : "使用祕訣", "Personal info" : "個人資訊", "Sync clients" : "同步客戶端", "Unlimited" : "無限", "__language_name__" : "__language_name__", "Verifying" : "驗證中", "Verifying …" : "驗證中…", "Verify" : "驗證", "%s enter the database username and name." : "%s 輸入資料庫名稱及使用者名稱", "%s enter the database username." : "%s 輸入資料庫使用者名稱", "%s enter the database name." : "%s 輸入資料庫名稱", "%s you may not use dots in the database name" : "%s 資料庫名稱不能包含小數點", "Oracle connection could not be established" : "無法建立 Oracle 資料庫連線", "Oracle username and/or password not valid" : "Oracle 用戶名和/或密碼無效", "PostgreSQL username and/or password not valid" : "PostgreSQL 用戶名和/或密碼無效", "You need to enter details of an existing account." : "您必須輸入現有帳號的資訊", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "不支援 Mac OS X 而且 %s 在這個平台上面無法正常運作,請自行衡量風險後使用!", "For the best results, please consider using a GNU/Linux server instead." : "請考慮使用 GNU/Linux 伺服器以獲得最佳體驗", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "看起來 %s 是在 32 位元的 PHP 環境運行,並且 php.ini 中被設置了 open_basedir 參數,這將讓超過 4GB 的檔案操作發生問題,強烈建議您更改設定。", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "請移除 php.ini 中的 open_basedir 設定,或是改用 64 位元的 PHP", "Set an admin username." : "設定管理員帳號", "Set an admin password." : "設定管理員密碼", "Can't create or write into the data directory %s" : "無法建立或寫入資料目錄 %s", "Invalid Federated Cloud ID" : "無效的雲端聯邦 ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "分享 %s失敗,不允許分享這樣的 %i 類別", "Sharing %s failed, because the file does not exist" : "分享 %s 失敗,因為檔案不存在", "You are not allowed to share %s" : "你不被允許分享 %s", "Sharing %s failed, because you can not share with yourself" : "分享 %s 失敗,不能分享給自己", "Sharing %s failed, because the user %s does not exist" : "分享 %s 失敗,因為使用者 %s 不存在", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "分享 %s 失敗,使用者 %s 並不屬於該項目擁有者 %s 所隸屬的任何一個群組", "Sharing %s failed, because this item is already shared with %s" : "分享 %s 失敗,因為此項目目前已經與 %s 分享", "Sharing %s failed, because this item is already shared with user %s" : "分享 %s 失敗,因為此項目目前已經與 %s 分享", "Sharing %s failed, because the group %s does not exist" : "分享 %s 失敗,因為群組 %s 不存在", "Sharing %s failed, because %s is not a member of the group %s" : "分享 %s 失敗,因為 %s 不是群組 %s 的一員", "You need to provide a password to create a public link, only protected links are allowed" : "您必須為公開連結設定一組密碼,我們只允許受密碼保護的連結", "Sharing %s failed, because sharing with links is not allowed" : "分享 %s 失敗,因為目前不允許使用連結分享", "Not allowed to create a federated share with the same user" : "不允許與同一個使用者建立聯邦分享", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "分享%s失敗,找不到%s,或許目前無法連線到該伺服器", "Share type %s is not valid for %s" : "分享類型 %s 對於 %s 來說無效", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "無法設定到期日,在分享之後,到期日不能設定為 %s 之後", "Cannot set expiration date. Expiration date is in the past" : "無法設定過去的日期為到期日", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "分享後端 %s 必須實作 OCP\\Share_Backend 界面", "Sharing backend %s not found" : "找不到分享後端 %s", "Sharing backend for %s not found" : "找不到 %s 的分享後端", "Sharing failed, because the user %s is the original sharer" : "分享失敗,因為使用者 %s 即是原本的分享者", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "分享 %s 失敗,因為權限設定超出了授權給 %s 的範圍", "Sharing %s failed, because resharing is not allowed" : "分享 %s 失敗,不允許重複分享", "Sharing %s failed, because the sharing backend for %s could not find its source" : "分享 %s 失敗,因為 %s 的分享後端找不到它的來源", "Sharing %s failed, because the file could not be found in the file cache" : "分享 %s 失敗,因為在快取中找不到該檔案", "Can’t increase permissions of %s" : "無法增加 %s 的權限", "Files can’t be shared with delete permissions" : "無法分享具有刪除權限的檔案", "Files can’t be shared with create permissions" : "無法分享具有新建權限的檔案", "Expiration date is in the past" : "到期日為過去的日期", "Can’t set expiration date more than %s days in the future" : "到期日不能設定為 %s 天以後的日期", "%s shared »%s« with you" : "%s 與您分享了 %s", "%s shared »%s« with you." : "%s 與您分享了 %s", "Click the button below to open it." : "點下方連結開啟", "Open »%s«" : "開啟 »%s«", "%s via %s" : "%s 經由 %s", "The requested share does not exist anymore" : "該分享已經不存在", "Could not find category \"%s\"" : "找不到分類:\"%s\"", "Sunday" : "週日", "Monday" : "週一", "Tuesday" : "週二", "Wednesday" : "週三", "Thursday" : "週四", "Friday" : "週五", "Saturday" : "週六", "Sun." : "日", "Mon." : "一", "Tue." : "二", "Wed." : "三", "Thu." : "四", "Fri." : "五", "Sat." : "六", "Su" : "日", "Mo" : "一", "Tu" : "二", "We" : "三", "Th" : "四", "Fr" : "五", "Sa" : "六", "January" : "一月", "February" : "二月", "March" : "三月", "April" : "四月", "May" : "五月", "June" : "六月", "July" : "七月", "August" : "八月", "September" : "九月", "October" : "十月", "November" : "十一月", "December" : "十二月", "Jan." : "一月", "Feb." : "二月", "Mar." : "三月", "Apr." : "四月", "May." : "五月", "Jun." : "六月", "Jul." : "七月", "Aug." : "八月", "Sep." : "九月", "Oct." : "十月", "Nov." : "十一月", "Dec." : "十二月", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "使用者名稱當中只能包含下列字元:\"a-z\", \"A-Z\", \"0-9\", 和 \"_.@-'\"", "A valid username must be provided" : "必須提供一個有效的用戶名", "Username contains whitespace at the beginning or at the end" : "用戶名的開頭或結尾有空白", "Username must not consist of dots only" : "使用者名稱不能只包含小數點", "A valid password must be provided" : "一定要提供一個有效的密碼", "The username is already being used" : "這個使用者名稱已經有人使用了", "Could not create user" : "無法建立使用者", "User disabled" : "使用者已停用", "Login canceled by app" : "應用程式取消了登入", "No app name specified" : "沒有指定應用程式名稱", "App '%s' could not be installed!" : "無法安裝應用程式 \"%s\"", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "應用程式 \"%s\" 無法被安裝,缺少下列所需元件: %s", "a safe home for all your data" : "您資料的安全屋", "File is currently busy, please try again later" : "檔案目前忙碌中,請稍候再試", "Can't read file" : "無法讀取檔案", "Application is not enabled" : "應用程式未啟用", "Authentication error" : "認證錯誤", "Token expired. Please reload page." : "Token 過期,請重新整理頁面。", "Unknown user" : "未知的使用者", "No database drivers (sqlite, mysql, or postgresql) installed." : "沒有安裝資料庫驅動程式 (sqlite, mysql, 或 postgresql)", "Cannot write into \"config\" directory" : "無法寫入 config 目錄", "Cannot write into \"apps\" directory" : "無法寫入 apps 目錄", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "開放網頁伺服器存取 apps 目錄,或是在設定檔中關閉 appstore 功能通常就可以修正這個問題,詳見 %s", "Cannot create \"data\" directory" : "無法建立 \"data\" 目錄", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "開放網頁伺服器存取根目錄通常就可以修正這個問題,詳見 %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "開放網頁伺服器存取根目錄通常就可以修正權限問題,詳見 %s", "Setting locale to %s failed" : "設定語系為 %s 失敗", "Please install one of these locales on your system and restart your webserver." : "請在系統中安裝這些語系的其中一個,然後重啓網頁伺服器", "Please ask your server administrator to install the module." : "請詢問系統管理員來安裝這些模組", "PHP module %s not installed." : "未安裝 PHP 模組 %s", "PHP setting \"%s\" is not set to \"%s\"." : "PHP 設定值 \"%s\" 沒有被設定為 \"%s\"", "Adjusting this setting in php.ini will make Nextcloud run again" : "調整 php.ini 中的設定,使 Nextcloud 重新運作", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload 應該要被設定成 \"0\" 而不是目前的設定 \"%s\" ", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "為了修正這個問題,請到 php.ini 將 <code>mbstring.func_overload</code> 的值改為 <code>0</code>", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 版本最低需求為 2.7.0。目前安裝版本為 %s 。", "To fix this issue update your libxml2 version and restart your web server." : "修正方式為更新您的 libxml2 為 2.7.0 以上版本,再重啟網頁伺服器。", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP 已經設定成「剪除 inline doc block」模式,這將會使幾個核心應用程式無法使用", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "這大概是由快取或是加速器像是 Zend OPcache, eAccelerator 造成的", "PHP modules have been installed, but they are still listed as missing?" : "你已經安裝了指定的 PHP 模組,可是還是顯示為找不到嗎?", "Please ask your server administrator to restart the web server." : "請聯絡您的系統管理員重新啟動網頁伺服器", "PostgreSQL >= 9 required" : "需要 PostgreSQL 版本 >= 9", "Please upgrade your database version" : "請升級您的資料庫版本", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "請將該目錄權限設定為 0770 ,以免其他使用者讀取目錄列表", "Your data directory is readable by other users" : "您的資料目錄可以被其他使用者讀取", "Your data directory must be an absolute path" : "您的資料目錄必須為絕對路徑", "Check the value of \"datadirectory\" in your configuration" : "請檢查您的設定檔中 \"datadirectory\" 的值", "Your data directory is invalid" : "您的資料目錄無效", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "請確保資料目錄最上層有一個 \".ocdata\" 檔案", "Could not obtain lock type %d on \"%s\"." : "無法取得鎖定:類型 %d ,檔案 %s", "Storage unauthorized. %s" : "儲存空間未經授權。%s", "Storage incomplete configuration. %s" : "儲存空間配置尚未完成。%s", "Storage connection error. %s" : "儲存空間連線錯誤。%s", "Storage is temporarily not available" : "儲存空間暫時無法使用", "Storage connection timeout. %s" : "儲存空間連線逾時。%s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "%s 允許網頁伺服器寫入設定目錄 %s 通常可以解決這個問題", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "名為 %s 的模組不存在,請在應用程式設定中啟用,或是聯絡系統管理員", "Server settings" : "伺服器設定", "DB Error: \"%s\"" : "資料庫錯誤:\"%s\"", "Offending command was: \"%s\"" : "有問題的指令是:\"%s\"", "You need to enter either an existing account or the administrator." : "您必須輸入一個現有的帳號或管理員帳號。", "Offending command was: \"%s\", name: %s, password: %s" : "有問題的指令是:\"%s\" ,使用者:\"%s\",密碼:\"%s\"", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "為 %s 設定權限失敗,因為欲設定的權限超出開放給 %s 的範圍", "Setting permissions for %s failed, because the item was not found" : "為 %s 設定權限失敗,因為找不到該項目", "Cannot clear expiration date. Shares are required to have an expiration date." : "到期日期不能空白,必須設定到期日才才能分享", "Cannot increase permissions of %s" : "無法增加%s的權限", "Files can't be shared with delete permissions" : "無法分享具有刪除權限的檔案", "Files can't be shared with create permissions" : "無法分享具有新建權限的檔案", "Cannot set expiration date more than %s days in the future" : "無法設定到期日超過未來%s天", "Personal" : "個人", "Admin" : "管理", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "通常藉由%s開放網頁伺服器對 apps 目錄的權限%s或是在設定檔中關閉 appstore 就可以修正這個問題", "Cannot create \"data\" directory (%s)" : "無法建立 \"data\" 目錄 (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">開放網頁伺服器寫入根目錄</a>通常就可以解決這個問題。", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "通常藉由%s開放網頁伺服器對根目錄的權限%s就可以修正權限問題", "Data directory (%s) is readable by other users" : "資料目錄 (%s) 可以被其他使用者讀取", "Data directory (%s) must be an absolute path" : "資料夾目錄(%s) 必須式絕對路徑", "Data directory (%s) is invalid" : "資料目錄 (%s) 無效", "Please check that the data directory contains a file \".ocdata\" in its root." : "請確保資料目錄最上層有一個 \".ocdata\" 檔案" }, "nplurals=1; plural=0;"); l10n/zh_TW.json 0000604 00000047200 15247130447 0007252 0 ustar 00 { "translations": { "Cannot write into \"config\" directory!" : "無法寫入 \"config\" 目錄!", "This can usually be fixed by giving the webserver write access to the config directory" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題", "See %s" : "見 %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題,詳見 %s", "Sample configuration detected" : "偵測到範本設定", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "看來您直接複製了範本設定來使用,這可能會毀掉你的安裝,請閱讀說明文件後對 config.php 進行適當的修改", "PHP %s or higher is required." : "需要 PHP %s 或更高版本", "PHP with a version lower than %s is required." : "需要 PHP 版本低於 %s ", "%sbit or higher PHP required." : "%s 或需要更高階版本的php", "Following databases are supported: %s" : "支援下列資料庫: %s", "The command line tool %s could not be found" : "找不到命令列工具指令 %s", "The library %s is not available." : "套件庫 %s 無法使用", "Library %s with a version higher than %s is required - available version %s." : "需要套件庫 %s 版本高於 %s - 可使用的版本是 %s", "Library %s with a version lower than %s is required - available version %s." : "需要套件庫 %s 版本低於 %s - 可使用的版本是 %s", "Following platforms are supported: %s" : "支援下列平台: %s", "Server version %s or higher is required." : "需要伺服器版本 %s 或更高", "Server version %s or lower is required." : "需要伺服器版本 %s 或更低", "Unknown filetype" : "未知的檔案類型", "Invalid image" : "無效的圖片", "Avatar image is not square" : "頭像不是正方形", "today" : "今天", "yesterday" : "昨天", "_%n day ago_::_%n days ago_" : ["%n 天前"], "last month" : "上個月", "_%n month ago_::_%n months ago_" : ["%n 個月前"], "last year" : "去年", "_%n year ago_::_%n years ago_" : ["%n 幾年前"], "_%n hour ago_::_%n hours ago_" : ["%n 小時前"], "_%n minute ago_::_%n minutes ago_" : ["%n 分鐘前"], "seconds ago" : "幾秒前", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "名為 %s 的模組不存在,請在應用程式設定中啟用,或是聯絡系統管理員", "File name is a reserved word" : "檔案名稱是保留字", "File name contains at least one invalid character" : "檔案名稱含有不允許的字元", "File name is too long" : "檔案名稱太長", "Dot files are not allowed" : "不允許小數點開頭的檔案", "Empty filename is not allowed" : "不允許空白的檔名", "App \"%s\" cannot be installed because appinfo file cannot be read." : "應用程式 \"%s\" 無法安裝,因為無法讀取 appinfo 檔案。", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "應用程式 \"%s\" 無法安裝,因為該應用程式不相容於目前版本的伺服器。", "This is an automatically sent email, please do not reply." : "此為自動寄送的電子郵件,請不要回覆。", "Help" : "說明", "Apps" : "應用程式", "Settings" : "設定", "Log out" : "登出", "Users" : "使用者", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "基本設定", "Sharing" : "分享", "Security" : "安全性", "Encryption" : "加密", "Additional settings" : "其他設定", "Tips & tricks" : "使用祕訣", "Personal info" : "個人資訊", "Sync clients" : "同步客戶端", "Unlimited" : "無限", "__language_name__" : "__language_name__", "Verifying" : "驗證中", "Verifying …" : "驗證中…", "Verify" : "驗證", "%s enter the database username and name." : "%s 輸入資料庫名稱及使用者名稱", "%s enter the database username." : "%s 輸入資料庫使用者名稱", "%s enter the database name." : "%s 輸入資料庫名稱", "%s you may not use dots in the database name" : "%s 資料庫名稱不能包含小數點", "Oracle connection could not be established" : "無法建立 Oracle 資料庫連線", "Oracle username and/or password not valid" : "Oracle 用戶名和/或密碼無效", "PostgreSQL username and/or password not valid" : "PostgreSQL 用戶名和/或密碼無效", "You need to enter details of an existing account." : "您必須輸入現有帳號的資訊", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "不支援 Mac OS X 而且 %s 在這個平台上面無法正常運作,請自行衡量風險後使用!", "For the best results, please consider using a GNU/Linux server instead." : "請考慮使用 GNU/Linux 伺服器以獲得最佳體驗", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "看起來 %s 是在 32 位元的 PHP 環境運行,並且 php.ini 中被設置了 open_basedir 參數,這將讓超過 4GB 的檔案操作發生問題,強烈建議您更改設定。", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "請移除 php.ini 中的 open_basedir 設定,或是改用 64 位元的 PHP", "Set an admin username." : "設定管理員帳號", "Set an admin password." : "設定管理員密碼", "Can't create or write into the data directory %s" : "無法建立或寫入資料目錄 %s", "Invalid Federated Cloud ID" : "無效的雲端聯邦 ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "分享 %s失敗,不允許分享這樣的 %i 類別", "Sharing %s failed, because the file does not exist" : "分享 %s 失敗,因為檔案不存在", "You are not allowed to share %s" : "你不被允許分享 %s", "Sharing %s failed, because you can not share with yourself" : "分享 %s 失敗,不能分享給自己", "Sharing %s failed, because the user %s does not exist" : "分享 %s 失敗,因為使用者 %s 不存在", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "分享 %s 失敗,使用者 %s 並不屬於該項目擁有者 %s 所隸屬的任何一個群組", "Sharing %s failed, because this item is already shared with %s" : "分享 %s 失敗,因為此項目目前已經與 %s 分享", "Sharing %s failed, because this item is already shared with user %s" : "分享 %s 失敗,因為此項目目前已經與 %s 分享", "Sharing %s failed, because the group %s does not exist" : "分享 %s 失敗,因為群組 %s 不存在", "Sharing %s failed, because %s is not a member of the group %s" : "分享 %s 失敗,因為 %s 不是群組 %s 的一員", "You need to provide a password to create a public link, only protected links are allowed" : "您必須為公開連結設定一組密碼,我們只允許受密碼保護的連結", "Sharing %s failed, because sharing with links is not allowed" : "分享 %s 失敗,因為目前不允許使用連結分享", "Not allowed to create a federated share with the same user" : "不允許與同一個使用者建立聯邦分享", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "分享%s失敗,找不到%s,或許目前無法連線到該伺服器", "Share type %s is not valid for %s" : "分享類型 %s 對於 %s 來說無效", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "無法設定到期日,在分享之後,到期日不能設定為 %s 之後", "Cannot set expiration date. Expiration date is in the past" : "無法設定過去的日期為到期日", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "分享後端 %s 必須實作 OCP\\Share_Backend 界面", "Sharing backend %s not found" : "找不到分享後端 %s", "Sharing backend for %s not found" : "找不到 %s 的分享後端", "Sharing failed, because the user %s is the original sharer" : "分享失敗,因為使用者 %s 即是原本的分享者", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "分享 %s 失敗,因為權限設定超出了授權給 %s 的範圍", "Sharing %s failed, because resharing is not allowed" : "分享 %s 失敗,不允許重複分享", "Sharing %s failed, because the sharing backend for %s could not find its source" : "分享 %s 失敗,因為 %s 的分享後端找不到它的來源", "Sharing %s failed, because the file could not be found in the file cache" : "分享 %s 失敗,因為在快取中找不到該檔案", "Can’t increase permissions of %s" : "無法增加 %s 的權限", "Files can’t be shared with delete permissions" : "無法分享具有刪除權限的檔案", "Files can’t be shared with create permissions" : "無法分享具有新建權限的檔案", "Expiration date is in the past" : "到期日為過去的日期", "Can’t set expiration date more than %s days in the future" : "到期日不能設定為 %s 天以後的日期", "%s shared »%s« with you" : "%s 與您分享了 %s", "%s shared »%s« with you." : "%s 與您分享了 %s", "Click the button below to open it." : "點下方連結開啟", "Open »%s«" : "開啟 »%s«", "%s via %s" : "%s 經由 %s", "The requested share does not exist anymore" : "該分享已經不存在", "Could not find category \"%s\"" : "找不到分類:\"%s\"", "Sunday" : "週日", "Monday" : "週一", "Tuesday" : "週二", "Wednesday" : "週三", "Thursday" : "週四", "Friday" : "週五", "Saturday" : "週六", "Sun." : "日", "Mon." : "一", "Tue." : "二", "Wed." : "三", "Thu." : "四", "Fri." : "五", "Sat." : "六", "Su" : "日", "Mo" : "一", "Tu" : "二", "We" : "三", "Th" : "四", "Fr" : "五", "Sa" : "六", "January" : "一月", "February" : "二月", "March" : "三月", "April" : "四月", "May" : "五月", "June" : "六月", "July" : "七月", "August" : "八月", "September" : "九月", "October" : "十月", "November" : "十一月", "December" : "十二月", "Jan." : "一月", "Feb." : "二月", "Mar." : "三月", "Apr." : "四月", "May." : "五月", "Jun." : "六月", "Jul." : "七月", "Aug." : "八月", "Sep." : "九月", "Oct." : "十月", "Nov." : "十一月", "Dec." : "十二月", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "使用者名稱當中只能包含下列字元:\"a-z\", \"A-Z\", \"0-9\", 和 \"_.@-'\"", "A valid username must be provided" : "必須提供一個有效的用戶名", "Username contains whitespace at the beginning or at the end" : "用戶名的開頭或結尾有空白", "Username must not consist of dots only" : "使用者名稱不能只包含小數點", "A valid password must be provided" : "一定要提供一個有效的密碼", "The username is already being used" : "這個使用者名稱已經有人使用了", "Could not create user" : "無法建立使用者", "User disabled" : "使用者已停用", "Login canceled by app" : "應用程式取消了登入", "No app name specified" : "沒有指定應用程式名稱", "App '%s' could not be installed!" : "無法安裝應用程式 \"%s\"", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "應用程式 \"%s\" 無法被安裝,缺少下列所需元件: %s", "a safe home for all your data" : "您資料的安全屋", "File is currently busy, please try again later" : "檔案目前忙碌中,請稍候再試", "Can't read file" : "無法讀取檔案", "Application is not enabled" : "應用程式未啟用", "Authentication error" : "認證錯誤", "Token expired. Please reload page." : "Token 過期,請重新整理頁面。", "Unknown user" : "未知的使用者", "No database drivers (sqlite, mysql, or postgresql) installed." : "沒有安裝資料庫驅動程式 (sqlite, mysql, 或 postgresql)", "Cannot write into \"config\" directory" : "無法寫入 config 目錄", "Cannot write into \"apps\" directory" : "無法寫入 apps 目錄", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "開放網頁伺服器存取 apps 目錄,或是在設定檔中關閉 appstore 功能通常就可以修正這個問題,詳見 %s", "Cannot create \"data\" directory" : "無法建立 \"data\" 目錄", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "開放網頁伺服器存取根目錄通常就可以修正這個問題,詳見 %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "開放網頁伺服器存取根目錄通常就可以修正權限問題,詳見 %s", "Setting locale to %s failed" : "設定語系為 %s 失敗", "Please install one of these locales on your system and restart your webserver." : "請在系統中安裝這些語系的其中一個,然後重啓網頁伺服器", "Please ask your server administrator to install the module." : "請詢問系統管理員來安裝這些模組", "PHP module %s not installed." : "未安裝 PHP 模組 %s", "PHP setting \"%s\" is not set to \"%s\"." : "PHP 設定值 \"%s\" 沒有被設定為 \"%s\"", "Adjusting this setting in php.ini will make Nextcloud run again" : "調整 php.ini 中的設定,使 Nextcloud 重新運作", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload 應該要被設定成 \"0\" 而不是目前的設定 \"%s\" ", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "為了修正這個問題,請到 php.ini 將 <code>mbstring.func_overload</code> 的值改為 <code>0</code>", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 版本最低需求為 2.7.0。目前安裝版本為 %s 。", "To fix this issue update your libxml2 version and restart your web server." : "修正方式為更新您的 libxml2 為 2.7.0 以上版本,再重啟網頁伺服器。", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP 已經設定成「剪除 inline doc block」模式,這將會使幾個核心應用程式無法使用", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "這大概是由快取或是加速器像是 Zend OPcache, eAccelerator 造成的", "PHP modules have been installed, but they are still listed as missing?" : "你已經安裝了指定的 PHP 模組,可是還是顯示為找不到嗎?", "Please ask your server administrator to restart the web server." : "請聯絡您的系統管理員重新啟動網頁伺服器", "PostgreSQL >= 9 required" : "需要 PostgreSQL 版本 >= 9", "Please upgrade your database version" : "請升級您的資料庫版本", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "請將該目錄權限設定為 0770 ,以免其他使用者讀取目錄列表", "Your data directory is readable by other users" : "您的資料目錄可以被其他使用者讀取", "Your data directory must be an absolute path" : "您的資料目錄必須為絕對路徑", "Check the value of \"datadirectory\" in your configuration" : "請檢查您的設定檔中 \"datadirectory\" 的值", "Your data directory is invalid" : "您的資料目錄無效", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "請確保資料目錄最上層有一個 \".ocdata\" 檔案", "Could not obtain lock type %d on \"%s\"." : "無法取得鎖定:類型 %d ,檔案 %s", "Storage unauthorized. %s" : "儲存空間未經授權。%s", "Storage incomplete configuration. %s" : "儲存空間配置尚未完成。%s", "Storage connection error. %s" : "儲存空間連線錯誤。%s", "Storage is temporarily not available" : "儲存空間暫時無法使用", "Storage connection timeout. %s" : "儲存空間連線逾時。%s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "%s 允許網頁伺服器寫入設定目錄 %s 通常可以解決這個問題", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "名為 %s 的模組不存在,請在應用程式設定中啟用,或是聯絡系統管理員", "Server settings" : "伺服器設定", "DB Error: \"%s\"" : "資料庫錯誤:\"%s\"", "Offending command was: \"%s\"" : "有問題的指令是:\"%s\"", "You need to enter either an existing account or the administrator." : "您必須輸入一個現有的帳號或管理員帳號。", "Offending command was: \"%s\", name: %s, password: %s" : "有問題的指令是:\"%s\" ,使用者:\"%s\",密碼:\"%s\"", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "為 %s 設定權限失敗,因為欲設定的權限超出開放給 %s 的範圍", "Setting permissions for %s failed, because the item was not found" : "為 %s 設定權限失敗,因為找不到該項目", "Cannot clear expiration date. Shares are required to have an expiration date." : "到期日期不能空白,必須設定到期日才才能分享", "Cannot increase permissions of %s" : "無法增加%s的權限", "Files can't be shared with delete permissions" : "無法分享具有刪除權限的檔案", "Files can't be shared with create permissions" : "無法分享具有新建權限的檔案", "Cannot set expiration date more than %s days in the future" : "無法設定到期日超過未來%s天", "Personal" : "個人", "Admin" : "管理", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "通常藉由%s開放網頁伺服器對 apps 目錄的權限%s或是在設定檔中關閉 appstore 就可以修正這個問題", "Cannot create \"data\" directory (%s)" : "無法建立 \"data\" 目錄 (%s)", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">開放網頁伺服器寫入根目錄</a>通常就可以解決這個問題。", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "通常藉由%s開放網頁伺服器對根目錄的權限%s就可以修正權限問題", "Data directory (%s) is readable by other users" : "資料目錄 (%s) 可以被其他使用者讀取", "Data directory (%s) must be an absolute path" : "資料夾目錄(%s) 必須式絕對路徑", "Data directory (%s) is invalid" : "資料目錄 (%s) 無效", "Please check that the data directory contains a file \".ocdata\" in its root." : "請確保資料目錄最上層有一個 \".ocdata\" 檔案" },"pluralForm" :"nplurals=1; plural=0;" } l10n/nl.js 0000604 00000054236 15247130447 0006302 0 ustar 00 OC.L10N.register( "lib", { "Cannot write into \"config\" directory!" : "Kan niet schrijven naar de \"config\" directory", "This can usually be fixed by giving the webserver write access to the config directory" : "Dit kan opgelost worden door de config map op de webserver schrijfrechten te geven", "See %s" : "Zie %s", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dit kan opgelost worden door de config map op de webserver schrijf rechten te geven. See %s", "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "De bestanden van de app %$1s zijn niet correct vervangen. Zorg ervoor dat de app versie compatibel is met de server.", "Sample configuration detected" : "Voorbeeld configuratie gevonden", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Er is gedetecteerd dat de voorbeeld configuratie is gekopieerd. Dit kan je installatie beschadigen en wordt dan ook niet ondersteund. Lees de documentatie voordat je wijzigingen aan config.php doorvoert", "%1$s and %2$s" : "%1$s en %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s en %3$s", "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s en %4$s", "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s en %5$s", "Education Edition" : "Onderwijs Editie", "Enterprise bundle" : "Zakelijke bundel", "Groupware bundle" : "Groupware bundel", "Social sharing bundle" : "Sociaal delen bundel", "PHP %s or higher is required." : "PHP %s of hoger vereist.", "PHP with a version lower than %s is required." : "PHP met een versie lager dan %s is vereist.", "%sbit or higher PHP required." : "%sbit of hogere PHP versie vereist.", "Following databases are supported: %s" : "De volgende databases worden ondersteund: %s", "The command line tool %s could not be found" : "Commandoregel tool %s is niet gevonden", "The library %s is not available." : "Library %s is niet beschikbaar.", "Library %s with a version higher than %s is required - available version %s." : "Library %s met een versienummer hoger dan %s is vereist - beschikbare versie %s.", "Library %s with a version lower than %s is required - available version %s." : "Library %s met een versienummer lager dan %s is vereist - beschikbare versie %s.", "Following platforms are supported: %s" : "De volgende platformen worden ondersteund: %s", "Server version %s or higher is required." : "Serverversie %s of hoger vereist.", "Server version %s or lower is required." : "Serverversie %s of lager vereist.", "Unknown filetype" : "Onbekend bestandsformaat", "Invalid image" : "Ongeldige afbeelding", "Avatar image is not square" : "Avatar afbeelding is niet vierkant", "today" : "vandaag", "yesterday" : "gisteren", "_%n day ago_::_%n days ago_" : ["%n dag geleden","%n dagen geleden"], "last month" : "vorige maand", "_%n month ago_::_%n months ago_" : ["%n maand geleden","%n maanden geleden"], "last year" : "vorig jaar", "_%n year ago_::_%n years ago_" : ["%n jaar geleden","%n jaren geleden"], "_%n hour ago_::_%n hours ago_" : ["%n uur geleden","%n uren geleden"], "_%n minute ago_::_%n minutes ago_" : ["%n minuut geleden","%n minuten geleden"], "seconds ago" : "seconden geleden", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module met ID: %s bestaat niet. Schakel die in binnen de app-instellingen of neem contact op met je beheerder.", "File name is a reserved word" : "Bestandsnaam is een gereserveerd woord", "File name contains at least one invalid character" : "De bestandsnaam bevat in ieder geval één verboden teken", "File name is too long" : "De bestandsnaam is te lang", "Dot files are not allowed" : "Punt bestanden zijn niet toegestaan", "Empty filename is not allowed" : "Een lege bestandsnaam is niet toegestaan", "App \"%s\" cannot be installed because appinfo file cannot be read." : "App \"%s\" kan niet worden geïnstalleerd, omdat het app info bestand niet gelezen kan worden.", "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "App \"%s\" kan niet worden geïnstalleerd, omdat deze niet compatible is met deze versie van de server.", "This is an automatically sent email, please do not reply." : "Dit is een automatisch gegenereerde e-mail, dus reageren is niet mogelijk.", "Help" : "Help", "Apps" : "Apps", "Settings" : "Instellingen", "Log out" : "Uitloggen", "Users" : "Gebruikers", "APCu" : "APCu", "Redis" : "Redis", "Basic settings" : "Basis instellingen", "Sharing" : "Delen", "Security" : "Beveiliging", "Encryption" : "Versleuteling", "Additional settings" : "Aanvullende instellingen", "Tips & tricks" : "Tips & trucs", "Personal info" : "Persoonlijke informatie", "Sync clients" : "Synchronisatie clients", "Unlimited" : "Ongelimiteerd", "__language_name__" : "Nederlands", "Verifying" : "Verifiëren", "Verifying …" : "Verifiëren...", "Verify" : "Verifieer", "%s enter the database username and name." : "%s voer de database gebruikersnaam en naam in .", "%s enter the database username." : "%s voer de database gebruikersnaam in.", "%s enter the database name." : "%s voer de databasenaam in.", "%s you may not use dots in the database name" : "%s er mogen geen punten in de databasenaam voorkomen", "Oracle connection could not be established" : "Er kon geen verbinding met Oracle worden gemaakt.", "Oracle username and/or password not valid" : "Oracle gebruikersnaam en/of wachtwoord ongeldig", "PostgreSQL username and/or password not valid" : "PostgreSQL gebruikersnaam en/of wachtwoord ongeldig", "You need to enter details of an existing account." : "Geef de details van een bestaand account op.", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OSX wordt niet ondersteund en %s zal niet goed werken op dit platform. Gebruik het op eigen risico!", "For the best results, please consider using a GNU/Linux server instead." : "Voor het beste resultaat adviseren wij het gebruik van een GNU/Linux server.", "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Het lijkt erop dat deze %s versie draait in een 32 bits PHP omgeving en dat open_basedir is geconfigureerd in php.ini. Dat zal leiden tot problemen met bestanden groter dan 4 GB en wordt dus sterk afgeraden.", "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Verwijder de open_basedir instelling in php.ini of schakel over op de 64bit PHP.", "Set an admin username." : "Stel de gebruikersnaam van de beheerder in.", "Set an admin password." : "Stel een beheerders wachtwoord in.", "Can't create or write into the data directory %s" : "Kan niets creëren of wegschrijven in de datadirectory %s", "Invalid Federated Cloud ID" : "Ongeldige gefedereerde Cloud ID", "Sharing %s failed, because the backend does not allow shares from type %i" : "Delen van %s is mislukt, omdat de share-backend het niet toestaat om type %i te delen", "Sharing %s failed, because the file does not exist" : "Delen van %s is mislukt, omdat het bestand niet bestaat", "You are not allowed to share %s" : "Je bent niet bevoegd om %s te delen", "Sharing %s failed, because you can not share with yourself" : "Delen van %s is mislukt, omdat je niet met jezelf kan delen", "Sharing %s failed, because the user %s does not exist" : "Delen van %s is mislukt, omdat gebruiker %s niet bestaat", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "Delen van %s is mislukt, omdat gebruiker %s geen lid is van een groep waar %s lid van is", "Sharing %s failed, because this item is already shared with %s" : "Delen van %s is mislukt, omdat het object al wordt gedeeld met %s", "Sharing %s failed, because this item is already shared with user %s" : "Delen van %s is mislukt, omdat het object al wordt gedeeld met gebruiker %s", "Sharing %s failed, because the group %s does not exist" : "Delen van %s is mislukt, omdat de groep %s niet bestaat", "Sharing %s failed, because %s is not a member of the group %s" : "Delen van %s is mislukt, omdat %s geen lid is van groep %s", "You need to provide a password to create a public link, only protected links are allowed" : "Je moet een wachtwoord opgeven om een openbare koppeling te maken, alleen wachtwoord beveiligde links zijn toegestaan", "Sharing %s failed, because sharing with links is not allowed" : "Delen van %s is mislukt, omdat het delen doormiddel van een een link niet is toegestaan", "Not allowed to create a federated share with the same user" : "Het is niet toegestaan om een gefedereerd gedeelde folder te maken met dezelfde gebruiker.", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Delen van %s mislukt, kon %s niet vinden, misschien is de server tijdelijk niet bereikbaar.", "Share type %s is not valid for %s" : "Delen van type %s is niet geldig voor %s", "Cannot set expiration date. Shares cannot expire later than %s after they have been shared" : "Een vervaldatum kan niet worden ingesteld. Gedeelde folders kunnen niet vervallen na %s ", "Cannot set expiration date. Expiration date is in the past" : "Kon vervaldatum niet instellen. De vervaldatum ligt in het verleden", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "De gedeelde achtergrond %s moet de OCP\\Share_Backend interface implementeren", "Sharing backend %s not found" : "De gedeelde backend %s is niet gevonden", "Sharing backend for %s not found" : "De gedeelde backend voor %s is niet gevonden", "Sharing failed, because the user %s is the original sharer" : "Delen mislukt, omdat gebruiker %s de originele deler is", "Sharing %s failed, because the permissions exceed permissions granted to %s" : "Delen van %s is mislukt, omdat de rechten toegekend aan %s overschreden zijn.", "Sharing %s failed, because resharing is not allowed" : "Delen van %s is mislukt, omdat her-delen niet is toegestaan", "Sharing %s failed, because the sharing backend for %s could not find its source" : "Delen van %s is mislukt, omdat de gedeelde backend voor %s de bron niet kon vinden", "Sharing %s failed, because the file could not be found in the file cache" : "Delen van %s is mislukt, omdat het bestand niet in de bestand cache kon worden gevonden", "Can’t increase permissions of %s" : "Kan niet meer rechten geven aan %s", "Files can’t be shared with delete permissions" : "Bestanden kunnen niet worden gedeeld met verwijder permissies", "Files can’t be shared with create permissions" : "Bestanden kunnen niet worden gedeeld met 'creëer' permissies", "Expiration date is in the past" : "De vervaldatum ligt in het verleden", "Can’t set expiration date more than %s days in the future" : "Kan de vervaldatum niet meer dan %s dagen in de toekomst instellen", "%s shared »%s« with you" : "%s deelde »%s« met jou", "%s shared »%s« with you." : "%s deelde »%s« met jou.", "Click the button below to open it." : "Klik de onderstaande button om te openen.", "Open »%s«" : "Open »%s«", "%s via %s" : "%s via %s", "The requested share does not exist anymore" : "De toegang tot de gedeelde folder bestaat niet meer", "Could not find category \"%s\"" : "Kan categorie \"%s\" niet vinden", "Sunday" : "Zondag", "Monday" : "Maandag", "Tuesday" : "Dinsdag", "Wednesday" : "Woensdag", "Thursday" : "Donderdag", "Friday" : "Vrijdag", "Saturday" : "Zaterdag", "Sun." : "Zo.", "Mon." : "Ma.", "Tue." : "Di.", "Wed." : "Wo.", "Thu." : "Do.", "Fri." : "Vr.", "Sat." : "Za.", "Su" : "Zo", "Mo" : "Ma", "Tu" : "Di", "We" : "Wo", "Th" : "Do", "Fr" : "Vr", "Sa" : "Za", "January" : "Januari", "February" : "Februari", "March" : "Maart", "April" : "April", "May" : "Mei", "June" : "Juni", "July" : "Juli", "August" : "Augustus", "September" : "September", "October" : "Oktober", "November" : "November", "December" : "December", "Jan." : "Jan.", "Feb." : "Feb.", "Mar." : "Mrt.", "Apr." : "Apr.", "May." : "Mei", "Jun." : "Jun.", "Jul." : "Jul.", "Aug." : "Aug.", "Sep." : "Sep.", "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Alleen de volgende tekens zijn toegestaan in een gebruikersnaam: \"a-z\", \"A-Z\", \"0-9\", en \"_.@-\"", "A valid username must be provided" : "Er moet een geldige gebruikersnaam worden opgegeven", "Username contains whitespace at the beginning or at the end" : "De gebruikersnaam bevat spaties aan het begin of aan het eind", "Username must not consist of dots only" : "De gebruikersnaam mag niet uit alleen punten bestaan", "A valid password must be provided" : "Er moet een geldig wachtwoord worden opgegeven", "The username is already being used" : "De gebruikersnaam bestaat al", "Could not create user" : "Kan gebruiker niet aanmaken.", "User disabled" : "Gebruiker geblokkeerd", "Login canceled by app" : "Inloggen geannuleerd door app", "No app name specified" : "Geen app naam opgegeven.", "App '%s' could not be installed!" : "App '%s' kan niet worden geïnstalleerd!", "App \"%s\" cannot be installed because the following dependencies are not fulfilled: %s" : "App \"%s\" kan niet worden geïnstalleerd, omdat de volgende afhankelijkheden nodig zijn: %s", "a safe home for all your data" : "een veilige plek voor al je gegevens", "File is currently busy, please try again later" : "Bestandsverwerking bezig, probeer het later opnieuw", "Can't read file" : "Kan bestand niet lezen", "Application is not enabled" : "De applicatie is niet ingeschakeld", "Authentication error" : "Authenticatie fout", "Token expired. Please reload page." : "Token verlopen. Herlaad de pagina.", "Unknown user" : "Onbekende gebruiker", "No database drivers (sqlite, mysql, or postgresql) installed." : "Geen database drivers (sqlite, mysql of postgres) geïnstalleerd.", "Cannot write into \"config\" directory" : "Kan niet schrijven naar de \"config\" directory", "Cannot write into \"apps\" directory" : "Kan niet schrijven naar de \"apps\" directory", "This can usually be fixed by giving the webserver write access to the apps directory or disabling the appstore in the config file. See %s" : "Dit kan hersteld worden door de app map schrijf rechten te geven iin de webserver of schakel de appstore uit bij het config bestand. Zie %s", "Cannot create \"data\" directory" : "\"data\" map kan niet worden aangemaakt", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dit kan hersteld worden door de root map schrijf rechten te geven op de webserver. Zie %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Rechten kunnen worden hersteld door de root map op de webserver schrijf toegang te geven. Zie %s.", "Setting locale to %s failed" : "Instellen taal op %s mislukte", "Please install one of these locales on your system and restart your webserver." : "Installeer één van de talen op je systeem en herstart je webserver.", "Please ask your server administrator to install the module." : "Vraag je beheerder om de module te installeren.", "PHP module %s not installed." : "PHP module %s niet geïnstalleerd.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP instelling \"%s\" staat niet op \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Het aanpassen van deze instelling in php.ini zorgt ervoor dat Nextcloud weer start", "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload is ingesteld op \"%s\" in plaats van de verwachte waarde \"0\"", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Om dit probleem op te lossen stel je in php.ini <code>mbstring.func_overload</code> in op <code>0</code>", "libxml2 2.7.0 is at least required. Currently %s is installed." : "De minimale versie van libxml2 versie is 2.7.0. Momenteel is versie%s geïnstalleerd.", "To fix this issue update your libxml2 version and restart your web server." : "Om dit probleem op te lossen, moet je de libxml2 versie bijwerken en je webserver herstarten.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP is nu zo ingesteld dat 'inline doc blocks' worden gestript. Hierdoor worden verschillende hoofd modules onbruikbaar.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dit wordt vermoedelijk veroorzaakt door een cache/accelerator, zoals Zend OPcache of eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP modules zijn geïnstalleerd, maar ze worden nog steeds als ontbrekend aangegeven?", "Please ask your server administrator to restart the web server." : "Vraag je beheerder de webserver opnieuw te starten.", "PostgreSQL >= 9 required" : "PostgreSQL >= 9 is vereist", "Please upgrade your database version" : "Werk je database versie bij", "Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Wijzig de permissie in 0770 zodat de directory niet door andere gebruikers bekeken kan worden.", "Your data directory is readable by other users" : "Je data map is leesbaar voor andere gebruikers", "Your data directory must be an absolute path" : "Je data map moet een absolute bestandslocatie hebben", "Check the value of \"datadirectory\" in your configuration" : "Controleer de waarde van \"datadirectory\" in je configuratie", "Your data directory is invalid" : "Je data folder is ongeldig", "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Zorg dat er een bestand genaamd \".ocdata\" in de hoofddirectory aanwezig is.", "Could not obtain lock type %d on \"%s\"." : "Kon geen lock type %d krijgen op \"%s\".", "Storage unauthorized. %s" : "Opslag niet toegestaan. %s", "Storage incomplete configuration. %s" : "Incomplete opslag configuratie. %s", "Storage connection error. %s" : "Opslag verbindingsfout. %s", "Storage is temporarily not available" : "Opslag is tijdelijk niet beschikbaar", "Storage connection timeout. %s" : "Opslag verbinding time-out. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Dit kan hersteld worden door de webserver %sschrijfrechten te geven op de configuratie directory%s", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module met id: %s bestaat niet. Activeer het in je apps instellingen, of neem contact op met je beheerder.", "Server settings" : "Server instellingen", "DB Error: \"%s\"" : "DB Fout: \"%s\"", "Offending command was: \"%s\"" : "Onjuiste commande was: \"%s\"", "You need to enter either an existing account or the administrator." : "Geef een bestaand account op of het beheerdersaccount.", "Offending command was: \"%s\", name: %s, password: %s" : "Onjuiste commando was: \"%s\", naam: %s, wachtwoord: %s", "Setting permissions for %s failed, because the permissions exceed permissions granted to %s" : "Instellen van de gebruik rechten voor %s is mislukt, omdat de rechten hoger zijn dan de aan %s toegekende gebruik rechten", "Setting permissions for %s failed, because the item was not found" : "Instellen van de gebruik rechten voor %s is mislukt, omdat het object niet is gevonden", "Cannot clear expiration date. Shares are required to have an expiration date." : "Kan verval datum niet weghalen. Gedeelte folders moeten een vervaldatum hebben.", "Cannot increase permissions of %s" : "Kan de rechten van %s niet verhogen.", "Files can't be shared with delete permissions" : "Bestanden kunnen niet worden gedeeld met verwijder rechten", "Files can't be shared with create permissions" : "Bestanden kunnen niet worden gedeeld met creëer rechten", "Cannot set expiration date more than %s days in the future" : "Kan de vervaldatum niet meer dan %s dagen in de toekomst instellen", "Personal" : "Persoonlijk", "Admin" : "Beheerder", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Dit kan hersteld worden door de webserver schrijfrechten te %s geven op de appsdirectory %s of door de appstore te deactiveren in het configuratie bestand.", "Cannot create \"data\" directory (%s)" : "Kan de \"data\" directory (%s) niet aanmaken", "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Dit kan hersteld worden door <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> de webserver schrijfrechten te geven tot de hoofd directory</a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Toegang kan hersteld worden door %s in de hoofd directory %s op de webserver schrijfrechten te geven.", "Data directory (%s) is readable by other users" : "De data directory (%s) is alleen lezen voor andere gebruikers", "Data directory (%s) must be an absolute path" : "De data directory (%s) moet een absolute bestand locatie hebben", "Data directory (%s) is invalid" : "Data directory (%s) is ongeldig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Verifieer dat de data directory een bestand \".ocdata\" in de hoofdmap heeft." }, "nplurals=2; plural=(n != 1);"); autoloader.php 0000604 00000012322 15247130447 0007417 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Markus Goetz <markus@woboq.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use \OCP\AutoloadNotAllowedException; class Autoloader { /** @var bool */ private $useGlobalClassPath = true; /** @var array */ private $validRoots = []; /** * Optional low-latency memory cache for class to path mapping. * * @var \OC\Memcache\Cache */ protected $memoryCache; /** * Autoloader constructor. * * @param string[] $validRoots */ public function __construct(array $validRoots) { foreach ($validRoots as $root) { $this->validRoots[$root] = true; } } /** * Add a path to the list of valid php roots for auto loading * * @param string $root */ public function addValidRoot($root) { $root = stream_resolve_include_path($root); $this->validRoots[$root] = true; } /** * disable the usage of the global classpath \OC::$CLASSPATH */ public function disableGlobalClassPath() { $this->useGlobalClassPath = false; } /** * enable the usage of the global classpath \OC::$CLASSPATH */ public function enableGlobalClassPath() { $this->useGlobalClassPath = true; } /** * get the possible paths for a class * * @param string $class * @return array|bool an array of possible paths or false if the class is not part of ownCloud */ public function findClass($class) { $class = trim($class, '\\'); $paths = array(); if ($this->useGlobalClassPath && array_key_exists($class, \OC::$CLASSPATH)) { $paths[] = \OC::$CLASSPATH[$class]; /** * @TODO: Remove this when necessary * Remove "apps/" from inclusion path for smooth migration to multi app dir */ if (strpos(\OC::$CLASSPATH[$class], 'apps/') === 0) { \OCP\Util::writeLog('core', 'include path for class "' . $class . '" starts with "apps/"', \OCP\Util::DEBUG); $paths[] = str_replace('apps/', '', \OC::$CLASSPATH[$class]); } } elseif (strpos($class, 'OC_') === 0) { $paths[] = \OC::$SERVERROOT . '/lib/private/legacy/' . strtolower(str_replace('_', '/', substr($class, 3)) . '.php'); } elseif (strpos($class, 'OCA\\') === 0) { list(, $app, $rest) = explode('\\', $class, 3); $app = strtolower($app); $appPath = \OC_App::getAppPath($app); if ($appPath && stream_resolve_include_path($appPath)) { $paths[] = $appPath . '/' . strtolower(str_replace('\\', '/', $rest) . '.php'); // If not found in the root of the app directory, insert '/lib' after app id and try again. $paths[] = $appPath . '/lib/' . strtolower(str_replace('\\', '/', $rest) . '.php'); } } elseif ($class === 'Test\\TestCase') { // This File is considered public API, so we make sure that the class // can still be loaded, although the PSR-4 paths have not been loaded. $paths[] = \OC::$SERVERROOT . '/tests/lib/TestCase.php'; } return $paths; } /** * @param string $fullPath * @return bool */ protected function isValidPath($fullPath) { foreach ($this->validRoots as $root => $true) { if (substr($fullPath, 0, strlen($root) + 1) === $root . '/') { return true; } } throw new AutoloadNotAllowedException($fullPath); } /** * Load the specified class * * @param string $class * @return bool */ public function load($class) { $pathsToRequire = null; if ($this->memoryCache) { $pathsToRequire = $this->memoryCache->get($class); } if(class_exists($class, false)) { return false; } if (!is_array($pathsToRequire)) { // No cache or cache miss $pathsToRequire = array(); foreach ($this->findClass($class) as $path) { $fullPath = stream_resolve_include_path($path); if ($fullPath && $this->isValidPath($fullPath)) { $pathsToRequire[] = $fullPath; } } if ($this->memoryCache) { $this->memoryCache->set($class, $pathsToRequire, 60); // cache 60 sec } } foreach ($pathsToRequire as $fullPath) { require_once $fullPath; } return false; } /** * Sets the optional low-latency cache for class to path mapping. * * @param \OC\Memcache\Cache $memoryCache Instance of memory cache. */ public function setMemoryCache(\OC\Memcache\Cache $memoryCache = null) { $this->memoryCache = $memoryCache; } } base.php 0000604 00000110776 15247130447 0006206 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Adam Williamson <awilliam@redhat.com> * @author Andreas Fischer <bantu@owncloud.com> * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Christoph Wurst <christoph@owncloud.com> * @author davidgumberg <davidnoizgumberg@gmail.com> * @author Florin Peter <github@florin-peter.de> * @author Georg Ehrke <georg@owncloud.com> * @author Hugo Gonzalez Labrador <hglavra@gmail.com> * @author Individual IT Services <info@individual-it.net> * @author Jakob Sack <mail@jakobsack.de> * @author Joachim Bauch <bauch@struktur.de> * @author Joachim Sokolowski <github@sokolowski.org> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Owen Winkler <a_github@midnightcircus.com> * @author Phil Davis <phil.davis@inf.org> * @author Ramiro Aparicio <rapariciog@gmail.com> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Pulzer <t.pulzer@kniel.de> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * @author Volkan Gezer <volkangezer@gmail.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ require_once 'public/Constants.php'; /** * Class that is a namespace for all global OC variables * No, we can not put this class in its own file because it is used by * OC_autoload! */ class OC { /** * Associative array for autoloading. classname => filename */ public static $CLASSPATH = array(); /** * The installation path for Nextcloud on the server (e.g. /srv/http/nextcloud) */ public static $SERVERROOT = ''; /** * the current request path relative to the Nextcloud root (e.g. files/index.php) */ private static $SUBURI = ''; /** * the Nextcloud root path for http requests (e.g. nextcloud/) */ public static $WEBROOT = ''; /** * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and * web path in 'url' */ public static $APPSROOTS = array(); /** * @var string */ public static $configDir; /** * requested app */ public static $REQUESTEDAPP = ''; /** * check if Nextcloud runs in cli mode */ public static $CLI = false; /** * @var \OC\Autoloader $loader */ public static $loader = null; /** @var \Composer\Autoload\ClassLoader $composerAutoloader */ public static $composerAutoloader = null; /** * @var \OC\Server */ public static $server = null; /** * @var \OC\Config */ private static $config = null; /** * @throws \RuntimeException when the 3rdparty directory is missing or * the app path list is empty or contains an invalid path */ public static function initPaths() { if(defined('PHPUNIT_CONFIG_DIR')) { self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/'; } elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) { self::$configDir = OC::$SERVERROOT . '/tests/config/'; } elseif($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { self::$configDir = rtrim($dir, '/') . '/'; } else { self::$configDir = OC::$SERVERROOT . '/config/'; } self::$config = new \OC\Config(self::$configDir); OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT))); /** * FIXME: The following lines are required because we can't yet instantiate * \OC::$server->getRequest() since \OC::$server does not yet exist. */ $params = [ 'server' => [ 'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'], 'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'], ], ]; $fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig(self::$config))); $scriptName = $fakeRequest->getScriptName(); if (substr($scriptName, -1) == '/') { $scriptName .= 'index.php'; //make sure suburi follows the same rules as scriptName if (substr(OC::$SUBURI, -9) != 'index.php') { if (substr(OC::$SUBURI, -1) != '/') { OC::$SUBURI = OC::$SUBURI . '/'; } OC::$SUBURI = OC::$SUBURI . 'index.php'; } } if (OC::$CLI) { OC::$WEBROOT = self::$config->getValue('overwritewebroot', ''); } else { if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) { OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI)); if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') { OC::$WEBROOT = '/' . OC::$WEBROOT; } } else { // The scriptName is not ending with OC::$SUBURI // This most likely means that we are calling from CLI. // However some cron jobs still need to generate // a web URL, so we use overwritewebroot as a fallback. OC::$WEBROOT = self::$config->getValue('overwritewebroot', ''); } // Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing // slash which is required by URL generation. if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT && substr($_SERVER['REQUEST_URI'], -1) !== '/') { header('Location: '.\OC::$WEBROOT.'/'); exit(); } } // search the apps folder $config_paths = self::$config->getValue('apps_paths', array()); if (!empty($config_paths)) { foreach ($config_paths as $paths) { if (isset($paths['url']) && isset($paths['path'])) { $paths['url'] = rtrim($paths['url'], '/'); $paths['path'] = rtrim($paths['path'], '/'); OC::$APPSROOTS[] = $paths; } } } elseif (file_exists(OC::$SERVERROOT . '/apps')) { OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true); } elseif (file_exists(OC::$SERVERROOT . '/../apps')) { OC::$APPSROOTS[] = array( 'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps', 'url' => '/apps', 'writable' => true ); } if (empty(OC::$APPSROOTS)) { throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder' . ' or the folder above. You can also configure the location in the config.php file.'); } $paths = array(); foreach (OC::$APPSROOTS as $path) { $paths[] = $path['path']; if (!is_dir($path['path'])) { throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the' . ' Nextcloud folder or the folder above. You can also configure the location in the' . ' config.php file.', $path['path'])); } } // set the right include path set_include_path( implode(PATH_SEPARATOR, $paths) ); } public static function checkConfig() { $l = \OC::$server->getL10N('lib'); // Create config if it does not already exist $configFilePath = self::$configDir .'/config.php'; if(!file_exists($configFilePath)) { @touch($configFilePath); } // Check if config is writable $configFileWritable = is_writable($configFilePath); if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled() || !$configFileWritable && self::checkUpgrade(false)) { $urlGenerator = \OC::$server->getURLGenerator(); if (self::$CLI) { echo $l->t('Cannot write into "config" directory!')."\n"; echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n"; echo "\n"; echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n"; exit; } else { OC_Template::printErrorPage( $l->t('Cannot write into "config" directory!'), $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ]) ); } } } public static function checkInstalled() { if (defined('OC_CONSOLE')) { return; } // Redirect to installer if not installed if (!\OC::$server->getSystemConfig()->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') { if (OC::$CLI) { throw new Exception('Not installed'); } else { $url = OC::$WEBROOT . '/index.php'; header('Location: ' . $url); } exit(); } } public static function checkMaintenanceMode() { // Allow ajax update script to execute without being stopped if (\OC::$server->getSystemConfig()->getValue('maintenance', false) && OC::$SUBURI != '/core/ajax/update.php') { // send http status 503 header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); header('Retry-After: 120'); // render error page $template = new OC_Template('', 'update.user', 'guest'); OC_Util::addScript('maintenance-check'); OC_Util::addStyle('core', 'guest'); $template->printPage(); die(); } } /** * Checks if the version requires an update and shows * @param bool $showTemplate Whether an update screen should get shown * @return bool|void */ public static function checkUpgrade($showTemplate = true) { if (\OCP\Util::needUpgrade()) { if (function_exists('opcache_reset')) { opcache_reset(); } $systemConfig = \OC::$server->getSystemConfig(); if ($showTemplate && !$systemConfig->getValue('maintenance', false)) { self::printUpgradePage(); exit(); } else { return true; } } return false; } /** * Prints the upgrade page */ private static function printUpgradePage() { $systemConfig = \OC::$server->getSystemConfig(); $disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false); $tooBig = false; if (!$disableWebUpdater) { $apps = \OC::$server->getAppManager(); $tooBig = false; if ($apps->isInstalled('user_ldap')) { $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder(); $result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count') ->from('ldap_user_mapping') ->execute(); $row = $result->fetch(); $result->closeCursor(); $tooBig = ($row['user_count'] > 50); } if (!$tooBig && $apps->isInstalled('user_saml')) { $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder(); $result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count') ->from('user_saml_users') ->execute(); $row = $result->fetch(); $result->closeCursor(); $tooBig = ($row['user_count'] > 50); } if (!$tooBig) { // count users $stats = \OC::$server->getUserManager()->countUsers(); $totalUsers = array_sum($stats); $tooBig = ($totalUsers > 50); } } $ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) && $_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis'; if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) { // send http status 503 header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); header('Retry-After: 120'); // render error page $template = new OC_Template('', 'update.use-cli', 'guest'); $template->assign('productName', 'nextcloud'); // for now $template->assign('version', OC_Util::getVersionString()); $template->assign('tooBig', $tooBig); $template->printPage(); die(); } // check whether this is a core update or apps update $installedVersion = $systemConfig->getValue('version', '0.0.0'); $currentVersion = implode('.', \OCP\Util::getVersion()); // if not a core upgrade, then it's apps upgrade $isAppsOnlyUpgrade = (version_compare($currentVersion, $installedVersion, '=')); $oldTheme = $systemConfig->getValue('theme'); $systemConfig->setValue('theme', ''); OC_Util::addScript('config'); // needed for web root OC_Util::addScript('update'); /** @var \OC\App\AppManager $appManager */ $appManager = \OC::$server->getAppManager(); $tmpl = new OC_Template('', 'update.admin', 'guest'); $tmpl->assign('version', OC_Util::getVersionString()); $tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade); // get third party apps $ocVersion = \OCP\Util::getVersion(); $incompatibleApps = $appManager->getIncompatibleApps($ocVersion); $incompatibleShippedApps = []; foreach ($incompatibleApps as $appInfo) { if ($appManager->isShipped($appInfo['id'])) { $incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')'; } } if (!empty($incompatibleShippedApps)) { $l = \OC::$server->getL10N('core'); $hint = $l->t('The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]); throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint); } $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion)); $tmpl->assign('incompatibleAppsList', $incompatibleApps); $tmpl->assign('productName', 'Nextcloud'); // for now $tmpl->assign('oldTheme', $oldTheme); $tmpl->printPage(); } public static function initSession() { // prevents javascript from accessing php session cookies ini_set('session.cookie_httponly', true); // set the cookie path to the Nextcloud directory $cookie_path = OC::$WEBROOT ? : '/'; ini_set('session.cookie_path', $cookie_path); // Let the session name be changed in the initSession Hook $sessionName = OC_Util::getInstanceId(); try { // Allow session apps to create a custom session object $useCustomSession = false; $session = self::$server->getSession(); OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession)); if (!$useCustomSession) { // set the session name to the instance id - which is unique $session = new \OC\Session\Internal($sessionName); } $cryptoWrapper = \OC::$server->getSessionCryptoWrapper(); $session = $cryptoWrapper->wrapSession($session); self::$server->setSession($session); // if session can't be started break with http 500 error } catch (Exception $e) { \OCP\Util::logException('base', $e); //show the user a detailed error page OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR); OC_Template::printExceptionErrorPage($e); die(); } $sessionLifeTime = self::getSessionLifeTime(); // session timeout if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) { if (isset($_COOKIE[session_name()])) { setcookie(session_name(), null, -1, self::$WEBROOT ? : '/'); } \OC::$server->getUserSession()->logout(); } $session->set('LAST_ACTIVITY', time()); } /** * @return string */ private static function getSessionLifeTime() { return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24); } public static function loadAppClassPaths() { foreach (OC_App::getEnabledApps() as $app) { $appPath = OC_App::getAppPath($app); if ($appPath === false) { continue; } $file = $appPath . '/appinfo/classpath.php'; if (file_exists($file)) { require_once $file; } } } /** * Try to set some values to the required Nextcloud default */ public static function setRequiredIniValues() { @ini_set('default_charset', 'UTF-8'); @ini_set('gd.jpeg_ignore_warning', 1); } /** * Send the same site cookies */ private static function sendSameSiteCookies() { $cookieParams = session_get_cookie_params(); $secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : ''; $policies = [ 'lax', 'strict', ]; // Append __Host to the cookie if it meets the requirements $cookiePrefix = ''; if($cookieParams['secure'] === true && $cookieParams['path'] === '/') { $cookiePrefix = '__Host-'; } foreach($policies as $policy) { header( sprintf( 'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s', $cookiePrefix, $policy, $cookieParams['path'], $policy ), false ); } } /** * Same Site cookie to further mitigate CSRF attacks. This cookie has to * be set in every request if cookies are sent to add a second level of * defense against CSRF. * * If the cookie is not sent this will set the cookie and reload the page. * We use an additional cookie since we want to protect logout CSRF and * also we can't directly interfere with PHP's session mechanism. */ private static function performSameSiteCookieProtection() { $request = \OC::$server->getRequest(); // Some user agents are notorious and don't really properly follow HTTP // specifications. For those, have an automated opt-out. Since the protection // for remote.php is applied in base.php as starting point we need to opt out // here. $incompatibleUserAgents = [ // OS X Finder '/^WebDAVFS/', ]; if($request->isUserAgent($incompatibleUserAgents)) { return; } if(count($_COOKIE) > 0) { $requestUri = $request->getScriptName(); $processingScript = explode('/', $requestUri); $processingScript = $processingScript[count($processingScript)-1]; // FIXME: In a SAML scenario we don't get any strict or lax cookie // send for the ACS endpoint. Since we have some legacy code in Nextcloud // (direct PHP files) the enforcement of lax cookies is performed here // instead of the middleware. // // This means we cannot exclude some routes from the cookie validation, // which normally is not a problem but is a little bit cumbersome for // this use-case. // Once the old legacy PHP endpoints have been removed we can move // the verification into a middleware and also adds some exemptions. // // Questions about this code? Ask Lukas ;-) $currentUrl = substr(explode('?',$request->getRequestUri(), 2)[0], strlen(\OC::$WEBROOT)); if($currentUrl === '/index.php/apps/user_saml/saml/acs' || $currentUrl === '/apps/user_saml/saml/acs') { return; } // For the "index.php" endpoint only a lax cookie is required. if($processingScript === 'index.php') { if(!$request->passesLaxCookieCheck()) { self::sendSameSiteCookies(); header('Location: '.$_SERVER['REQUEST_URI']); exit(); } } else { // All other endpoints require the lax and the strict cookie if(!$request->passesStrictCookieCheck()) { self::sendSameSiteCookies(); // Debug mode gets access to the resources without strict cookie // due to the fact that the SabreDAV browser also lives there. if(!\OC::$server->getConfig()->getSystemValue('debug', false)) { http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE); exit(); } } } } elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) { self::sendSameSiteCookies(); } } public static function init() { // calculate the root directories OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4)); // register autoloader $loaderStart = microtime(true); require_once __DIR__ . '/autoloader.php'; self::$loader = new \OC\Autoloader([ OC::$SERVERROOT . '/lib/private/legacy', ]); if (defined('PHPUNIT_RUN')) { self::$loader->addValidRoot(OC::$SERVERROOT . '/tests'); } spl_autoload_register(array(self::$loader, 'load')); $loaderEnd = microtime(true); self::$CLI = (php_sapi_name() == 'cli'); // Add default composer PSR-4 autoloader self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php'; try { self::initPaths(); // setup 3rdparty autoloader $vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php'; if (!file_exists($vendorAutoLoad)) { throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".'); } require_once $vendorAutoLoad; } catch (\RuntimeException $e) { if (!self::$CLI) { $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']); $protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1'; header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE); } // we can't use the template error page here, because this needs the // DI container which isn't available yet print($e->getMessage()); exit(); } // setup the basic server self::$server = new \OC\Server(\OC::$WEBROOT, self::$config); \OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd); \OC::$server->getEventLogger()->start('boot', 'Initialize'); // Don't display errors and log them error_reporting(E_ALL | E_STRICT); @ini_set('display_errors', 0); @ini_set('log_errors', 1); if(!date_default_timezone_set('UTC')) { throw new \RuntimeException('Could not set timezone to UTC'); }; //try to configure php to enable big file uploads. //this doesn´t work always depending on the webserver and php configuration. //Let´s try to overwrite some defaults anyway //try to set the maximum execution time to 60min if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { @set_time_limit(3600); } @ini_set('max_execution_time', 3600); @ini_set('max_input_time', 3600); //try to set the maximum filesize to 10G @ini_set('upload_max_filesize', '10G'); @ini_set('post_max_size', '10G'); @ini_set('file_uploads', '50'); self::setRequiredIniValues(); self::handleAuthHeaders(); self::registerAutoloaderCache(); // initialize intl fallback is necessary \Patchwork\Utf8\Bootup::initIntl(); OC_Util::isSetLocaleWorking(); if (!defined('PHPUNIT_RUN')) { OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger()); $debug = \OC::$server->getConfig()->getSystemValue('debug', false); OC\Log\ErrorHandler::register($debug); } \OC::$server->getEventLogger()->start('init_session', 'Initialize session'); OC_App::loadApps(array('session')); if (!self::$CLI) { self::initSession(); } \OC::$server->getEventLogger()->end('init_session'); self::checkConfig(); self::checkInstalled(); OC_Response::addSecurityHeaders(); if(self::$server->getRequest()->getServerProtocol() === 'https') { ini_set('session.cookie_secure', true); } self::performSameSiteCookieProtection(); if (!defined('OC_CONSOLE')) { $errors = OC_Util::checkServer(\OC::$server->getSystemConfig()); if (count($errors) > 0) { if (self::$CLI) { // Convert l10n string into regular string for usage in database $staticErrors = []; foreach ($errors as $error) { echo $error['error'] . "\n"; echo $error['hint'] . "\n\n"; $staticErrors[] = [ 'error' => (string)$error['error'], 'hint' => (string)$error['hint'], ]; } try { \OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors)); } catch (\Exception $e) { echo('Writing to database failed'); } exit(1); } else { OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE); OC_Util::addStyle('guest'); OC_Template::printGuestPage('', 'error', array('errors' => $errors)); exit; } } elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) { \OC::$server->getConfig()->deleteAppValue('core', 'cronErrors'); } } //try to set the session lifetime $sessionLifeTime = self::getSessionLifeTime(); @ini_set('gc_maxlifetime', (string)$sessionLifeTime); $systemConfig = \OC::$server->getSystemConfig(); // User and Groups if (!$systemConfig->getValue("installed", false)) { self::$server->getSession()->set('user_id', ''); } OC_User::useBackend(new \OC\User\Database()); \OC::$server->getGroupManager()->addBackend(new \OC\Group\Database()); // Subscribe to the hook \OCP\Util::connectHook( '\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', '\OC\User\Database', 'preLoginNameUsedAsUserName' ); //setup extra user backends if (!self::checkUpgrade(false)) { OC_User::setupBackends(); } else { // Run upgrades in incognito mode OC_User::setIncognitoMode(true); } self::registerCacheHooks(); self::registerFilesystemHooks(); self::registerShareHooks(); self::registerLogRotate(); self::registerEncryptionWrapper(); self::registerEncryptionHooks(); self::registerAccountHooks(); self::registerSettingsHooks(); $settings = new \OC\Settings\Application(); $settings->register(); //make sure temporary files are cleaned up $tmpManager = \OC::$server->getTempManager(); register_shutdown_function(array($tmpManager, 'clean')); $lockProvider = \OC::$server->getLockingProvider(); register_shutdown_function(array($lockProvider, 'releaseAll')); // Check whether the sample configuration has been copied if($systemConfig->getValue('copied_sample_config', false)) { $l = \OC::$server->getL10N('lib'); header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); OC_Template::printErrorPage( $l->t('Sample configuration detected'), $l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php') ); return; } $request = \OC::$server->getRequest(); $host = $request->getInsecureServerHost(); /** * if the host passed in headers isn't trusted * FIXME: Should not be in here at all :see_no_evil: */ if (!OC::$CLI // overwritehost is always trusted, workaround to not have to make // \OC\AppFramework\Http\Request::getOverwriteHost public && self::$server->getConfig()->getSystemValue('overwritehost') === '' && !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host) && self::$server->getConfig()->getSystemValue('installed', false) ) { // Allow access to CSS resources $isScssRequest = false; if(strpos($request->getPathInfo(), '/css/') === 0) { $isScssRequest = true; } if (!$isScssRequest) { header('HTTP/1.1 400 Bad Request'); header('Status: 400 Bad Request'); \OC::$server->getLogger()->warning( 'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.', [ 'app' => 'core', 'remoteAddress' => $request->getRemoteAddress(), 'host' => $host, ] ); $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest'); $tmpl->assign('domain', $host); $tmpl->printPage(); exit(); } } \OC::$server->getEventLogger()->end('boot'); } /** * register hooks for the cache */ public static function registerCacheHooks() { //don't try to do this before we are properly setup if (\OC::$server->getSystemConfig()->getValue('installed', false) && !self::checkUpgrade(false)) { // NOTE: This will be replaced to use OCP $userSession = self::$server->getUserSession(); $userSession->listen('\OC\User', 'postLogin', function () { try { $cache = new \OC\Cache\File(); $cache->gc(); } catch (\OC\ServerNotAvailableException $e) { // not a GC exception, pass it on throw $e; } catch (\OC\ForbiddenException $e) { // filesystem blocked for this request, ignore } catch (\Exception $e) { // a GC exception should not prevent users from using OC, // so log the exception \OC::$server->getLogger()->warning('Exception when running cache gc: ' . $e->getMessage(), array('app' => 'core')); } }); } } public static function registerSettingsHooks() { $dispatcher = \OC::$server->getEventDispatcher(); $dispatcher->addListener(OCP\App\ManagerEvent::EVENT_APP_DISABLE, function($event) { /** @var \OCP\App\ManagerEvent $event */ \OC::$server->getSettingsManager()->onAppDisabled($event->getAppID()); }); $dispatcher->addListener(OCP\App\ManagerEvent::EVENT_APP_UPDATE, function($event) { /** @var \OCP\App\ManagerEvent $event */ $jobList = \OC::$server->getJobList(); $job = 'OC\\Settings\\RemoveOrphaned'; if(!($jobList->has($job, null))) { $jobList->add($job); } }); } private static function registerEncryptionWrapper() { $manager = self::$server->getEncryptionManager(); \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage'); } private static function registerEncryptionHooks() { $enabled = self::$server->getEncryptionManager()->isEnabled(); if ($enabled) { \OCP\Util::connectHook('OCP\Share', 'post_shared', 'OC\Encryption\HookManager', 'postShared'); \OCP\Util::connectHook('OCP\Share', 'post_unshare', 'OC\Encryption\HookManager', 'postUnshared'); \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OC\Encryption\HookManager', 'postRename'); \OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', 'OC\Encryption\HookManager', 'postRestore'); } } private static function registerAccountHooks() { $hookHandler = new \OC\Accounts\Hooks(\OC::$server->getLogger()); \OCP\Util::connectHook('OC_User', 'changeUser', $hookHandler, 'changeUserHook'); } /** * register hooks for the cache */ public static function registerLogRotate() { $systemConfig = \OC::$server->getSystemConfig(); if ($systemConfig->getValue('installed', false) && $systemConfig->getValue('log_rotate_size', false) && !self::checkUpgrade(false)) { //don't try to do this before we are properly setup //use custom logfile path if defined, otherwise use default of nextcloud.log in data directory \OC::$server->getJobList()->add('OC\Log\Rotate'); } } /** * register hooks for the filesystem */ public static function registerFilesystemHooks() { // Check for blacklisted files OC_Hook::connect('OC_Filesystem', 'write', 'OC\Files\Filesystem', 'isBlacklisted'); OC_Hook::connect('OC_Filesystem', 'rename', 'OC\Files\Filesystem', 'isBlacklisted'); } /** * register hooks for sharing */ public static function registerShareHooks() { if (\OC::$server->getSystemConfig()->getValue('installed')) { OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share20\Hooks', 'post_deleteUser'); OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OC\Share20\Hooks', 'post_removeFromGroup'); OC_Hook::connect('OC_User', 'post_deleteGroup', 'OC\Share20\Hooks', 'post_deleteGroup'); } } protected static function registerAutoloaderCache() { // The class loader takes an optional low-latency cache, which MUST be // namespaced. The instanceid is used for namespacing, but might be // unavailable at this point. Furthermore, it might not be possible to // generate an instanceid via \OC_Util::getInstanceId() because the // config file may not be writable. As such, we only register a class // loader cache if instanceid is available without trying to create one. $instanceId = \OC::$server->getSystemConfig()->getValue('instanceid', null); if ($instanceId) { try { $memcacheFactory = \OC::$server->getMemCacheFactory(); self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader')); } catch (\Exception $ex) { } } } /** * Handle the request */ public static function handleRequest() { \OC::$server->getEventLogger()->start('handle_request', 'Handle request'); $systemConfig = \OC::$server->getSystemConfig(); // load all the classpaths from the enabled apps so they are available // in the routing files of each app OC::loadAppClassPaths(); // Check if Nextcloud is installed or in maintenance (update) mode if (!$systemConfig->getValue('installed', false)) { \OC::$server->getSession()->clear(); $setupHelper = new OC\Setup(\OC::$server->getSystemConfig(), \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), \OC::$server->query(\OCP\Defaults::class), \OC::$server->getLogger(), \OC::$server->getSecureRandom()); $controller = new OC\Core\Controller\SetupController($setupHelper); $controller->run($_POST); exit(); } $request = \OC::$server->getRequest(); $requestPath = $request->getRawPathInfo(); if ($requestPath === '/heartbeat') { return; } if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade self::checkMaintenanceMode(); self::checkUpgrade(); } // emergency app disabling if ($requestPath === '/disableapp' && $request->getMethod() === 'POST' && ((array)$request->getParam('appid')) !== '' ) { \OCP\JSON::callCheck(); \OCP\JSON::checkAdminUser(); $appIds = (array)$request->getParam('appid'); foreach($appIds as $appId) { $appId = \OC_App::cleanAppId($appId); \OC_App::disable($appId); } \OC_JSON::success(); exit(); } // Always load authentication apps OC_App::loadApps(['authentication']); // Load minimum set of apps if (!self::checkUpgrade(false) && !$systemConfig->getValue('maintenance', false)) { // For logged-in users: Load everything if(\OC::$server->getUserSession()->isLoggedIn()) { OC_App::loadApps(); } else { // For guests: Load only filesystem and logging OC_App::loadApps(array('filesystem', 'logging')); self::handleLogin($request); } } if (!self::$CLI) { try { if (!$systemConfig->getValue('maintenance', false) && !self::checkUpgrade(false)) { OC_App::loadApps(array('filesystem', 'logging')); OC_App::loadApps(); } OC_Util::setupFS(); OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo()); return; } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { //header('HTTP/1.0 404 Not Found'); } catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) { OC_Response::setStatus(405); return; } } // Handle WebDAV if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') { // not allowed any more to prevent people // mounting this root directly. // Users need to mount remote.php/webdav instead. header('HTTP/1.1 405 Method Not Allowed'); header('Status: 405 Method Not Allowed'); return; } // Someone is logged in if (\OC::$server->getUserSession()->isLoggedIn()) { OC_App::loadApps(); OC_User::setupBackends(); OC_Util::setupFS(); // FIXME // Redirect to default application OC_Util::redirectToDefaultPage(); } else { // Not handled and not logged in header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm')); } } /** * Check login: apache auth, auth token, basic auth * * @param OCP\IRequest $request * @return boolean */ static function handleLogin(OCP\IRequest $request) { $userSession = self::$server->getUserSession(); if (OC_User::handleApacheAuth()) { return true; } if ($userSession->tryTokenLogin($request)) { return true; } if (isset($_COOKIE['nc_username']) && isset($_COOKIE['nc_token']) && isset($_COOKIE['nc_session_id']) && $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) { return true; } if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) { return true; } return false; } protected static function handleAuthHeaders() { //copy http auth headers for apache+php-fcgid work around if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) { $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION']; } // Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary. $vars = array( 'HTTP_AUTHORIZATION', // apache+php-cgi work around 'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative ); foreach ($vars as $var) { if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) { list($name, $password) = explode(':', base64_decode($matches[1]), 2); $_SERVER['PHP_AUTH_USER'] = $name; $_SERVER['PHP_AUTH_PW'] = $password; break; } } } } OC::init(); public/Activity/IConsumer.php 0000604 00000002561 15247130450 0012234 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Activity/IConsumer interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Activity; /** * Interface IConsumer * * @package OCP\Activity * @since 6.0.0 */ interface IConsumer { /** * @param IEvent $event * @return null * @since 6.0.0 * @since 8.2.0 Replaced the parameters with an IEvent object */ public function receive(IEvent $event); } public/Activity/ISetting.php 0000604 00000003700 15247130450 0012052 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Activity; /** * Interface ISetting * * @package OCP\Activity * @since 11.0.0 */ interface ISetting { /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier(); /** * @return string A translated string * @since 11.0.0 */ public function getName(); /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority(); /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream(); /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream(); /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail(); /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail(); } public/Activity/IManager.php 0000604 00000017307 15247130450 0012017 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Activity/IManager interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Activity; /** * Interface IManager * * @package OCP\Activity * @since 6.0.0 */ interface IManager { /** * Generates a new IEvent object * * Make sure to call at least the following methods before sending it to the * app with via the publish() method: * - setApp() * - setType() * - setAffectedUser() * - setSubject() * * @return IEvent * @since 8.2.0 */ public function generateEvent(); /** * Publish an event to the activity consumers * * Make sure to call at least the following methods before sending an Event: * - setApp() * - setType() * - setAffectedUser() * - setSubject() * * @param IEvent $event * @throws \BadMethodCallException if required values have not been set * @since 8.2.0 */ public function publish(IEvent $event); /** * @param string $app The app where this event is associated with * @param string $subject A short description of the event * @param array $subjectParams Array with parameters that are filled in the subject * @param string $message A longer description of the event * @param array $messageParams Array with parameters that are filled in the message * @param string $file The file including path where this event is associated with * @param string $link A link where this event is associated with * @param string $affectedUser Recipient of the activity * @param string $type Type of the notification * @param int $priority Priority of the notification * @since 6.0.0 * @deprecated 8.2.0 Grab an IEvent from generateEvent() instead and use the publish() method */ public function publishActivity($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority); /** * In order to improve lazy loading a closure can be registered which will be called in case * activity consumers are actually requested * * $callable has to return an instance of \OCP\Activity\IConsumer * * @param \Closure $callable * @return void * @since 6.0.0 */ public function registerConsumer(\Closure $callable); /** * In order to improve lazy loading a closure can be registered which will be called in case * activity consumers are actually requested * * $callable has to return an instance of \OCP\Activity\IExtension * * @param \Closure $callable * @return void * @since 8.0.0 */ public function registerExtension(\Closure $callable); /** * @param string $filter Class must implement OCA\Activity\IFilter * @return void * @since 11.0.0 */ public function registerFilter($filter); /** * @return IFilter[] * @since 11.0.0 */ public function getFilters(); /** * @param string $id * @return IFilter * @throws \InvalidArgumentException when the filter was not found * @since 11.0.0 */ public function getFilterById($id); /** * @param string $setting Class must implement OCA\Activity\ISetting * @return void * @since 11.0.0 */ public function registerSetting($setting); /** * @return ISetting[] * @since 11.0.0 */ public function getSettings(); /** * @param string $provider Class must implement OCA\Activity\IProvider * @return void * @since 11.0.0 */ public function registerProvider($provider); /** * @return IProvider[] * @since 11.0.0 */ public function getProviders(); /** * @param string $id * @return ISetting * @throws \InvalidArgumentException when the setting was not found * @since 11.0.0 */ public function getSettingById($id); /** * Will return additional notification types as specified by other apps * * @param string $languageCode * @return array Array "stringID of the type" => "translated string description for the setting" * or Array "stringID of the type" => [ * 'desc' => "translated string description for the setting" * 'methods' => [\OCP\Activity\IExtension::METHOD_*], * ] * @since 8.0.0 - 8.2.0: Added support to allow limiting notifications to certain methods * @deprecated 11.0.0 - Use getSettings() instead */ public function getNotificationTypes($languageCode); /** * @param string $method * @return array * @since 8.0.0 * @deprecated 11.0.0 - Use getSettings()->isDefaulEnabled<method>() instead */ public function getDefaultTypes($method); /** * @param string $type * @return string * @since 8.0.0 */ public function getTypeIcon($type); /** * @param string $type * @param int $id * @since 8.2.0 */ public function setFormattingObject($type, $id); /** * @return bool * @since 8.2.0 */ public function isFormattingFilteredObject(); /** * @param bool $status Set to true, when parsing events should not use SVG icons * @since 12.0.1 */ public function setRequirePNG($status); /** * @return bool * @since 12.0.1 */ public function getRequirePNG(); /** * @param string $app * @param string $text * @param array $params * @param boolean $stripPath * @param boolean $highlightParams * @param string $languageCode * @return string|false * @since 8.0.0 */ public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); /** * @param string $app * @param string $text * @return array|false * @since 8.0.0 */ public function getSpecialParameterList($app, $text); /** * @param array $activity * @return integer|false * @since 8.0.0 */ public function getGroupParameter($activity); /** * Set the user we need to use * * @param string|null $currentUserId * @throws \UnexpectedValueException If the user is invalid * @since 9.0.1 */ public function setCurrentUserId($currentUserId); /** * Get the user we need to use * * Either the user is logged in, or we try to get it from the token * * @return string * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique * @since 8.1.0 */ public function getCurrentUserId(); /** * @return array * @since 8.0.0 * @deprecated 11.0.0 - Use getFilters() instead */ public function getNavigation(); /** * @param string $filterValue * @return boolean * @since 8.0.0 * @deprecated 11.0.0 - Use getFilterById() instead */ public function isFilterValid($filterValue); /** * @param array $types * @param string $filter * @return array * @since 8.0.0 * @deprecated 11.0.0 - Use getFilterById()->filterTypes() instead */ public function filterNotificationTypes($types, $filter); /** * @param string $filter * @return array * @since 8.0.0 * @deprecated 11.0.0 - Use getFilterById() instead */ public function getQueryForFilter($filter); } public/Activity/IProvider.php 0000604 00000003112 15247130450 0012224 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Activity; /** * Interface IProvider * * @package OCP\Activity * @since 11.0.0 */ interface IProvider { /** * @param string $language The language which should be used for translating, e.g. "en" * @param IEvent $event The current event which should be parsed * @param IEvent|null $previousEvent A potential previous event which you can combine with the current one. * To do so, simply use setChildEvent($previousEvent) after setting the * combined subject on the current event. * @return IEvent * @throws \InvalidArgumentException Should be thrown if your provider does not know this event * @since 11.0.0 */ public function parse($language, IEvent $event, IEvent $previousEvent = null); } public/Activity/IExtension.php 0000604 00000012373 15247130450 0012417 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Activity/IExtension interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Activity; /** * Interface IExtension * * @package OCP\Activity * @since 8.0.0 */ interface IExtension { const METHOD_STREAM = 'stream'; const METHOD_MAIL = 'email'; const PRIORITY_VERYLOW = 10; const PRIORITY_LOW = 20; const PRIORITY_MEDIUM = 30; const PRIORITY_HIGH = 40; const PRIORITY_VERYHIGH = 50; /** * The extension can return an array of additional notification types. * If no additional types are to be added false is to be returned * * @param string $languageCode * @return array|false Array "stringID of the type" => "translated string description for the setting" * or Array "stringID of the type" => [ * 'desc' => "translated string description for the setting" * 'methods' => [self::METHOD_*], * ] * @since 8.0.0 - 8.2.0: Added support to allow limiting notifications to certain methods */ public function getNotificationTypes($languageCode); /** * For a given method additional types to be displayed in the settings can be returned. * In case no additional types are to be added false is to be returned. * * @param string $method * @return array|false * @since 8.0.0 */ public function getDefaultTypes($method); /** * A string naming the css class for the icon to be used can be returned. * If no icon is known for the given type false is to be returned. * * @param string $type * @return string|false * @since 8.0.0 */ public function getTypeIcon($type); /** * The extension can translate a given message to the requested languages. * If no translation is available false is to be returned. * * @param string $app * @param string $text * @param array $params * @param boolean $stripPath * @param boolean $highlightParams * @param string $languageCode * @return string|false * @since 8.0.0 */ public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); /** * The extension can define the type of parameters for translation * * Currently known types are: * * file => will strip away the path of the file and add a tooltip with it * * username => will add the avatar of the user * * email => will add a mailto link * * @param string $app * @param string $text * @return array|false * @since 8.0.0 */ public function getSpecialParameterList($app, $text); /** * The extension can define the parameter grouping by returning the index as integer. * In case no grouping is required false is to be returned. * * @param array $activity * @return integer|false * @since 8.0.0 */ public function getGroupParameter($activity); /** * The extension can define additional navigation entries. The array returned has to contain two keys 'top' * and 'apps' which hold arrays with the relevant entries. * If no further entries are to be added false is no be returned. * * @return array|false * @since 8.0.0 * @deprecated 11.0.0 - Register an IFilter instead */ public function getNavigation(); /** * The extension can check if a customer filter (given by a query string like filter=abc) is valid or not. * * @param string $filterValue * @return boolean * @since 8.0.0 * @deprecated 11.0.0 - Register an IFilter instead */ public function isFilterValid($filterValue); /** * The extension can filter the types based on the filter if required. * In case no filter is to be applied false is to be returned unchanged. * * @param array $types * @param string $filter * @return array|false * @since 8.0.0 * @deprecated 11.0.0 - Register an IFilter instead */ public function filterNotificationTypes($types, $filter); /** * For a given filter the extension can specify the sql query conditions including parameters for that query. * In case the extension does not know the filter false is to be returned. * The query condition and the parameters are to be returned as array with two elements. * E.g. return array('`app` = ? and `message` like ?', array('mail', 'ownCloud%')); * * @param string $filter * @return array|false * @since 8.0.0 * @deprecated 11.0.0 - Register an IFilter instead */ public function getQueryForFilter($filter); } public/Activity/IEventMerger.php 0000604 00000004376 15247130450 0012672 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Activity; /** * Interface EventMerger * * @package OCP\Activity * @since 11.0 */ interface IEventMerger { /** * Combines two events when possible to have grouping: * * Example1: Two events with subject '{user} created {file}' and * $mergeParameter file with different file and same user will be merged * to '{user} created {file1} and {file2}' and the childEvent on the return * will be set, if the events have been merged. * * Example2: Two events with subject '{user} created {file}' and * $mergeParameter file with same file and same user will be merged to * '{user} created {file1}' and the childEvent on the return will be set, if * the events have been merged. * * The following requirements have to be met, in order to be merged: * - Both events need to have the same `getApp()` * - Both events must not have a message `getMessage()` * - Both events need to have the same subject `getSubject()` * - Both events need to have the same object type `getObjectType()` * - The time difference between both events must not be bigger then 3 hours * - Only up to 5 events can be merged. * - All parameters apart from such starting with $mergeParameter must be * the same for both events. * * @param string $mergeParameter * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @since 11.0 */ public function mergeEvents($mergeParameter, IEvent $event, IEvent $previousEvent = null); } public/Activity/IEvent.php 0000604 00000014265 15247130450 0011526 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Activity/IEvent interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Activity; /** * Interface IEvent * * @package OCP\Activity * @since 8.2.0 */ interface IEvent { /** * Set the app of the activity * * @param string $app * @return IEvent * @throws \InvalidArgumentException if the app id is invalid * @since 8.2.0 */ public function setApp($app); /** * Set the type of the activity * * @param string $type * @return IEvent * @throws \InvalidArgumentException if the type is invalid * @since 8.2.0 */ public function setType($type); /** * Set the affected user of the activity * * @param string $user * @return IEvent * @throws \InvalidArgumentException if the affected user is invalid * @since 8.2.0 */ public function setAffectedUser($user); /** * Set the author of the activity * * @param string $author * @return IEvent * @throws \InvalidArgumentException if the author is invalid * @since 8.2.0 */ public function setAuthor($author); /** * Set the author of the activity * * @param int $timestamp * @return IEvent * @throws \InvalidArgumentException if the timestamp is invalid * @since 8.2.0 */ public function setTimestamp($timestamp); /** * Set the subject of the activity * * @param string $subject * @param array $parameters * @return IEvent * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 8.2.0 */ public function setSubject($subject, array $parameters = []); /** * @param string $subject * @return $this * @throws \InvalidArgumentException if the subject is invalid * @since 11.0.0 */ public function setParsedSubject($subject); /** * @return string * @since 11.0.0 */ public function getParsedSubject(); /** * @param string $subject * @param array $parameters * @return $this * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 11.0.0 */ public function setRichSubject($subject, array $parameters = []); /** * @return string * @since 11.0.0 */ public function getRichSubject(); /** * @return array[] * @since 11.0.0 */ public function getRichSubjectParameters(); /** * Set the message of the activity * * @param string $message * @param array $parameters * @return IEvent * @throws \InvalidArgumentException if the message or parameters are invalid * @since 8.2.0 */ public function setMessage($message, array $parameters = []); /** * @param string $message * @return $this * @throws \InvalidArgumentException if the message is invalid * @since 11.0.0 */ public function setParsedMessage($message); /** * @return string * @since 11.0.0 */ public function getParsedMessage(); /** * @param string $message * @param array $parameters * @return $this * @throws \InvalidArgumentException if the message or parameters are invalid * @since 11.0.0 */ public function setRichMessage($message, array $parameters = []); /** * @return string * @since 11.0.0 */ public function getRichMessage(); /** * @return array[] * @since 11.0.0 */ public function getRichMessageParameters(); /** * Set the object of the activity * * @param string $objectType * @param int $objectId * @param string $objectName * @return IEvent * @throws \InvalidArgumentException if the object is invalid * @since 8.2.0 */ public function setObject($objectType, $objectId, $objectName = ''); /** * Set the link of the activity * * @param string $link * @return IEvent * @throws \InvalidArgumentException if the link is invalid * @since 8.2.0 */ public function setLink($link); /** * @return string * @since 8.2.0 */ public function getApp(); /** * @return string * @since 8.2.0 */ public function getType(); /** * @return string * @since 8.2.0 */ public function getAffectedUser(); /** * @return string * @since 8.2.0 */ public function getAuthor(); /** * @return int * @since 8.2.0 */ public function getTimestamp(); /** * @return string * @since 8.2.0 */ public function getSubject(); /** * @return array * @since 8.2.0 */ public function getSubjectParameters(); /** * @return string * @since 8.2.0 */ public function getMessage(); /** * @return array * @since 8.2.0 */ public function getMessageParameters(); /** * @return string * @since 8.2.0 */ public function getObjectType(); /** * @return string * @since 8.2.0 */ public function getObjectId(); /** * @return string * @since 8.2.0 */ public function getObjectName(); /** * @return string * @since 8.2.0 */ public function getLink(); /** * @param string $icon * @return $this * @throws \InvalidArgumentException if the icon is invalid * @since 11.0.0 */ public function setIcon($icon); /** * @return string * @since 11.0.0 */ public function getIcon(); /** * @param IEvent $child * @since 11.0.0 */ public function setChildEvent(IEvent $child); /** * @return IEvent|null * @since 11.0.0 */ public function getChildEvent(); /** * @return bool * @since 11.0.0 */ public function isValid(); /** * @return bool * @since 11.0.0 */ public function isValidParsed(); } public/Activity/IFilter.php 0000604 00000003564 15247130450 0011672 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Activity; /** * Interface IFilter * * @package OCP\Activity * @since 11.0.0 */ interface IFilter { /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier(); /** * @return string A translated string * @since 11.0.0 */ public function getName(); /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority(); /** * @return string Full URL to an icon, empty string when none is given * @since 11.0.0 */ public function getIcon(); /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function filterTypes(array $types); /** * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function allowedApps(); } public/Encryption/Exceptions/GenericEncryptionException.php 0000604 00000003006 15247130450 0020310 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Clark Tomlinson <fallen013@gmail.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Encryption\Exceptions; use OC\HintException; /** * Class GenericEncryptionException * * @package OCP\Encryption\Exceptions * @since 8.1.0 */ class GenericEncryptionException extends HintException { /** * @param string $message * @param string $hint * @param int $code * @param \Exception $previous * @since 8.1.0 */ public function __construct($message = '', $hint = '', $code = 0, \Exception $previous = null) { if (empty($message)) { $message = 'Unspecified encryption exception'; } parent::__construct($message, $hint, $code, $previous); } } public/Encryption/Keys/IStorage.php 0000604 00000011072 15247130450 0013313 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Encryption\Keys; /** * Interface IStorage * * @package OCP\Encryption\Keys * @since 8.1.0 */ interface IStorage { /** * get user specific key * * @param string $uid ID if the user for whom we want the key * @param string $keyId id of the key * @param string $encryptionModuleId * * @return mixed key * @since 8.1.0 */ public function getUserKey($uid, $keyId, $encryptionModuleId); /** * get file specific key * * @param string $path path to file * @param string $keyId id of the key * @param string $encryptionModuleId * * @return mixed key * @since 8.1.0 */ public function getFileKey($path, $keyId, $encryptionModuleId); /** * get system-wide encryption keys not related to a specific user, * e.g something like a key for public link shares * * @param string $keyId id of the key * @param string $encryptionModuleId * * @return mixed key * @since 8.1.0 */ public function getSystemUserKey($keyId, $encryptionModuleId); /** * set user specific key * * @param string $uid ID if the user for whom we want the key * @param string $keyId id of the key * @param mixed $key * @param string $encryptionModuleId * @since 8.1.0 */ public function setUserKey($uid, $keyId, $key, $encryptionModuleId); /** * set file specific key * * @param string $path path to file * @param string $keyId id of the key * @param mixed $key * @param string $encryptionModuleId * @since 8.1.0 */ public function setFileKey($path, $keyId, $key, $encryptionModuleId); /** * set system-wide encryption keys not related to a specific user, * e.g something like a key for public link shares * * @param string $keyId id of the key * @param mixed $key * @param string $encryptionModuleId * * @return mixed key * @since 8.1.0 */ public function setSystemUserKey($keyId, $key, $encryptionModuleId); /** * delete user specific key * * @param string $uid ID if the user for whom we want to delete the key * @param string $keyId id of the key * @param string $encryptionModuleId * * @return boolean False when the key could not be deleted * @since 8.1.0 */ public function deleteUserKey($uid, $keyId, $encryptionModuleId); /** * delete file specific key * * @param string $path path to file * @param string $keyId id of the key * @param string $encryptionModuleId * * @return boolean False when the key could not be deleted * @since 8.1.0 */ public function deleteFileKey($path, $keyId, $encryptionModuleId); /** * delete all file keys for a given file * * @param string $path to the file * * @return boolean False when the keys could not be deleted * @since 8.1.0 */ public function deleteAllFileKeys($path); /** * delete system-wide encryption keys not related to a specific user, * e.g something like a key for public link shares * * @param string $keyId id of the key * @param string $encryptionModuleId * * @return boolean False when the key could not be deleted * @since 8.1.0 */ public function deleteSystemUserKey($keyId, $encryptionModuleId); /** * copy keys if a file was renamed * * @param string $source * @param string $target * @return boolean * @since 8.1.0 */ public function renameKeys($source, $target); /** * move keys if a file was renamed * * @param string $source * @param string $target * @return boolean * @since 8.1.0 */ public function copyKeys($source, $target); /** * backup keys of a given encryption module * * @param string $encryptionModuleId * @param string $purpose * @param string $uid * @return bool * @since 12.0.0 */ public function backupUserKeys($encryptionModuleId, $purpose, $uid); } public/Encryption/IFile.php 0000604 00000002173 15247130450 0011655 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Encryption; /** * Interface IFile * * @package OCP\Encryption * @since 8.1.0 */ interface IFile { /** * get list of users with access to the file * * @param string $path to the file * @return array * @since 8.1.0 */ public function getAccessList($path); } public/Encryption/IManager.php 0000604 00000004767 15247130450 0012363 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Encryption; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; use OC\Encryption\Exceptions\ModuleAlreadyExistsException; /** * This class provides access to files encryption apps. * * @since 8.1.0 */ interface IManager { /** * Check if encryption is available (at least one encryption module needs to be enabled) * * @return bool true if enabled, false if not * @since 8.1.0 */ public function isEnabled(); /** * Registers an callback function which must return an encryption module instance * * @param string $id * @param string $displayName * @param callable $callback * @throws ModuleAlreadyExistsException * @since 8.1.0 */ public function registerEncryptionModule($id, $displayName, callable $callback); /** * Unregisters an encryption module * * @param string $moduleId * @since 8.1.0 */ public function unregisterEncryptionModule($moduleId); /** * get a list of all encryption modules * * @return array [id => ['id' => $id, 'displayName' => $displayName, 'callback' => callback]] * @since 8.1.0 */ public function getEncryptionModules(); /** * get a specific encryption module * * @param string $moduleId Empty to get the default module * @return IEncryptionModule * @throws ModuleDoesNotExistsException * @since 8.1.0 */ public function getEncryptionModule($moduleId = ''); /** * get default encryption module Id * * @return string * @since 8.1.0 */ public function getDefaultEncryptionModuleId(); /** * set default encryption module Id * * @param string $moduleId * @return string * @since 8.1.0 */ public function setDefaultEncryptionModule($moduleId); } public/Encryption/IEncryptionModule.php 0000604 00000013027 15247130450 0014276 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Encryption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; /** * Interface IEncryptionModule * * @package OCP\Encryption * @since 8.1.0 */ interface IEncryptionModule { /** * @return string defining the technical unique id * @since 8.1.0 */ public function getId(); /** * In comparison to getKey() this function returns a human readable (maybe translated) name * * @return string * @since 8.1.0 */ public function getDisplayName(); /** * start receiving chunks from a file. This is the place where you can * perform some initial step before starting encrypting/decrypting the * chunks * * @param string $path to the file * @param string $user who read/write the file (null for public access) * @param string $mode php stream open mode * @param array $header contains the header data read from the file * @param array $accessList who has access to the file contains the key 'users' and 'public' * * $return array $header contain data as key-value pairs which should be * written to the header, in case of a write operation * or if no additional data is needed return a empty array * @since 8.1.0 */ public function begin($path, $user, $mode, array $header, array $accessList); /** * last chunk received. This is the place where you can perform some final * operation and return some remaining data if something is left in your * buffer. * * @param string $path to the file * @param string $position id of the last block (looks like "<Number>end") * * @return string remained data which should be written to the file in case * of a write operation * * @since 8.1.0 * @since 9.0.0 parameter $position added */ public function end($path, $position); /** * encrypt data * * @param string $data you want to encrypt * @param string $position position of the block we want to encrypt (starts with '0') * * @return mixed encrypted data * * @since 8.1.0 * @since 9.0.0 parameter $position added */ public function encrypt($data, $position); /** * decrypt data * * @param string $data you want to decrypt * @param string $position position of the block we want to decrypt * * @return mixed decrypted data * * @since 8.1.0 * @since 9.0.0 parameter $position added */ public function decrypt($data, $position); /** * update encrypted file, e.g. give additional users access to the file * * @param string $path path to the file which should be updated * @param string $uid of the user who performs the operation * @param array $accessList who has access to the file contains the key 'users' and 'public' * @return boolean * @since 8.1.0 */ public function update($path, $uid, array $accessList); /** * should the file be encrypted or not * * @param string $path * @return boolean * @since 8.1.0 */ public function shouldEncrypt($path); /** * get size of the unencrypted payload per block. * ownCloud read/write files with a block size of 8192 byte * * @param bool $signed * @return int * @since 8.1.0 optional parameter $signed was added in 9.0.0 */ public function getUnencryptedBlockSize($signed = false); /** * check if the encryption module is able to read the file, * e.g. if all encryption keys exists * * @param string $path * @param string $uid user for whom we want to check if he can read the file * @return boolean * @since 8.1.0 */ public function isReadable($path, $uid); /** * Initial encryption of all files * * @param InputInterface $input * @param OutputInterface $output write some status information to the terminal during encryption * @since 8.2.0 */ public function encryptAll(InputInterface $input, OutputInterface $output); /** * prepare encryption module to decrypt all files * * @param InputInterface $input * @param OutputInterface $output write some status information to the terminal during encryption * @param $user (optional) for which the files should be decrypted, default = all users * @return bool return false on failure or if it isn't supported by the module * @since 8.2.0 */ public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = ''); /** * Check if the module is ready to be used by that specific user. * In case a module is not ready - because e.g. key pairs have not been generated * upon login this method can return false before any operation starts and might * cause issues during operations. * * @param string $user * @return boolean * @since 9.1.0 */ public function isReadyForUser($user); } public/Comments/IComment.php 0000604 00000012605 15247130450 0012034 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Comments; /** * Interface IComment * * This class represents a comment * * @package OCP\Comments * @since 9.0.0 */ interface IComment { const MAX_MESSAGE_LENGTH = 1000; /** * returns the ID of the comment * * It may return an empty string, if the comment was not stored. * It is expected that the concrete Comment implementation gives an ID * by itself (e.g. after saving). * * @return string * @since 9.0.0 */ public function getId(); /** * sets the ID of the comment and returns itself * * It is only allowed to set the ID only, if the current id is an empty * string (which means it is not stored in a database, storage or whatever * the concrete implementation does), or vice versa. Changing a given ID is * not permitted and must result in an IllegalIDChangeException. * * @param string $id * @return IComment * @throws IllegalIDChangeException * @since 9.0.0 */ public function setId($id); /** * returns the parent ID of the comment * * @return string * @since 9.0.0 */ public function getParentId(); /** * sets the parent ID and returns itself * @param string $parentId * @return IComment * @since 9.0.0 */ public function setParentId($parentId); /** * returns the topmost parent ID of the comment * * @return string * @since 9.0.0 */ public function getTopmostParentId(); /** * sets the topmost parent ID and returns itself * * @param string $id * @return IComment * @since 9.0.0 */ public function setTopmostParentId($id); /** * returns the number of children * * @return int * @since 9.0.0 */ public function getChildrenCount(); /** * sets the number of children * * @param int $count * @return IComment * @since 9.0.0 */ public function setChildrenCount($count); /** * returns the message of the comment * * @return string * @since 9.0.0 */ public function getMessage(); /** * sets the message of the comment and returns itself * * When the given message length exceeds MAX_MESSAGE_LENGTH an * MessageTooLongException shall be thrown. * * @param string $message * @return IComment * @throws MessageTooLongException * @since 9.0.0 */ public function setMessage($message); /** * returns an array containing mentions that are included in the comment * * @return array each mention provides a 'type' and an 'id', see example below * @since 11.0.0 * * The return array looks like: * [ * [ * 'type' => 'user', * 'id' => 'citizen4' * ], * [ * 'type' => 'group', * 'id' => 'media' * ], * … * ] * */ public function getMentions(); /** * returns the verb of the comment * * @return string * @since 9.0.0 */ public function getVerb(); /** * sets the verb of the comment, e.g. 'comment' or 'like' * * @param string $verb * @return IComment * @since 9.0.0 */ public function setVerb($verb); /** * returns the actor type * * @return string * @since 9.0.0 */ public function getActorType(); /** * returns the actor ID * * @return string * @since 9.0.0 */ public function getActorId(); /** * sets (overwrites) the actor type and id * * @param string $actorType e.g. 'users' * @param string $actorId e.g. 'zombie234' * @return IComment * @since 9.0.0 */ public function setActor($actorType, $actorId); /** * returns the creation date of the comment. * * If not explicitly set, it shall default to the time of initialization. * * @return \DateTime * @since 9.0.0 */ public function getCreationDateTime(); /** * sets the creation date of the comment and returns itself * * @param \DateTime $dateTime * @return IComment * @since 9.0.0 */ public function setCreationDateTime(\DateTime $dateTime); /** * returns the date of the most recent child * * @return \DateTime * @since 9.0.0 */ public function getLatestChildDateTime(); /** * sets the date of the most recent child * * @param \DateTime $dateTime * @return IComment * @since 9.0.0 */ public function setLatestChildDateTime(\DateTime $dateTime); /** * returns the object type the comment is attached to * * @return string * @since 9.0.0 */ public function getObjectType(); /** * returns the object id the comment is attached to * * @return string * @since 9.0.0 */ public function getObjectId(); /** * sets (overwrites) the object of the comment * * @param string $objectType e.g. 'files' * @param string $objectId e.g. '16435' * @return IComment * @since 9.0.0 */ public function setObject($objectType, $objectId); } public/Comments/CommentsEntityEvent.php 0000604 00000004065 15247130450 0014306 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Comments; use Symfony\Component\EventDispatcher\Event; /** * Class CommentsEntityEvent * * @package OCP\Comments * @since 9.1.0 */ class CommentsEntityEvent extends Event { const EVENT_ENTITY = 'OCP\Comments\ICommentsManager::registerEntity'; /** @var string */ protected $event; /** @var \Closure[] */ protected $collections; /** * DispatcherEvent constructor. * * @param string $event * @since 9.1.0 */ public function __construct($event) { $this->event = $event; $this->collections = []; } /** * @param string $name * @param \Closure $entityExistsFunction The closure should take one * argument, which is the id of the entity, that comments * should be handled for. The return should then be bool, * depending on whether comments are allowed (true) or not. * @throws \OutOfBoundsException when the entity name is already taken * @since 9.1.0 */ public function addEntityCollection($name, \Closure $entityExistsFunction) { if (isset($this->collections[$name])) { throw new \OutOfBoundsException('Duplicate entity name "' . $name . '"'); } $this->collections[$name] = $entityExistsFunction; } /** * @return \Closure[] * @since 9.1.0 */ public function getEntityCollections() { return $this->collections; } } public/Comments/NotFoundException.php 0000604 00000001654 15247130450 0013736 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Comments; /** * Exception for not found entity * @since 9.0.0 */ class NotFoundException extends \Exception {} public/Comments/ICommentsManager.php 0000604 00000017512 15247130450 0013514 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Comments; use OCP\IUser; /** * Interface ICommentsManager * * This class manages the access to comments * * @package OCP\Comments * @since 9.0.0 */ interface ICommentsManager { /** * @const DELETED_USER type and id for a user that has been deleted * @see deleteReferencesOfActor * @since 9.0.0 * * To be used as replacement for user type actors in deleteReferencesOfActor(). * * User interfaces shall show "Deleted user" as display name, if needed. */ const DELETED_USER = 'deleted_users'; /** * returns a comment instance * * @param string $id the ID of the comment * @return IComment * @throws NotFoundException * @since 9.0.0 */ public function get($id); /** * returns the comment specified by the id and all it's child comments * * @param string $id * @param int $limit max number of entries to return, 0 returns all * @param int $offset the start entry * @return array * @since 9.0.0 * * The return array looks like this * [ * 'comment' => IComment, // root comment * 'replies' => * [ * 0 => * [ * 'comment' => IComment, * 'replies' => * [ * 0 => * [ * 'comment' => IComment, * 'replies' => [ … ] * ], * … * ] * ] * 1 => * [ * 'comment' => IComment, * 'replies'=> [ … ] * ], * … * ] * ] */ public function getTree($id, $limit = 0, $offset = 0); /** * returns comments for a specific object (e.g. a file). * * The sort order is always newest to oldest. * * @param string $objectType the object type, e.g. 'files' * @param string $objectId the id of the object * @param int $limit optional, number of maximum comments to be returned. if * not specified, all comments are returned. * @param int $offset optional, starting point * @param \DateTime $notOlderThan optional, timestamp of the oldest comments * that may be returned * @return IComment[] * @since 9.0.0 */ public function getForObject( $objectType, $objectId, $limit = 0, $offset = 0, \DateTime $notOlderThan = null ); /** * @param $objectType string the object type, e.g. 'files' * @param $objectId string the id of the object * @param \DateTime $notOlderThan optional, timestamp of the oldest comments * that may be returned * @return Int * @since 9.0.0 */ public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null); /** * Get the number of unread comments for all files in a folder * * @param int $folderId * @param IUser $user * @return array [$fileId => $unreadCount] * @since 12.0.0 */ public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user); /** * creates a new comment and returns it. At this point of time, it is not * saved in the used data storage. Use save() after setting other fields * of the comment (e.g. message or verb). * * @param string $actorType the actor type (e.g. 'users') * @param string $actorId a user id * @param string $objectType the object type the comment is attached to * @param string $objectId the object id the comment is attached to * @return IComment * @since 9.0.0 */ public function create($actorType, $actorId, $objectType, $objectId); /** * permanently deletes the comment specified by the ID * * When the comment has child comments, their parent ID will be changed to * the parent ID of the item that is to be deleted. * * @param string $id * @return bool * @since 9.0.0 */ public function delete($id); /** * saves the comment permanently * * if the supplied comment has an empty ID, a new entry comment will be * saved and the instance updated with the new ID. * * Otherwise, an existing comment will be updated. * * Throws NotFoundException when a comment that is to be updated does not * exist anymore at this point of time. * * @param IComment $comment * @return bool * @throws NotFoundException * @since 9.0.0 */ public function save(IComment $comment); /** * removes references to specific actor (e.g. on user delete) of a comment. * The comment itself must not get lost/deleted. * * A 'users' type actor (type and id) should get replaced by the * value of the DELETED_USER constant of this interface. * * @param string $actorType the actor type (e.g. 'users') * @param string $actorId a user id * @return boolean * @since 9.0.0 */ public function deleteReferencesOfActor($actorType, $actorId); /** * deletes all comments made of a specific object (e.g. on file delete) * * @param string $objectType the object type (e.g. 'files') * @param string $objectId e.g. the file id * @return boolean * @since 9.0.0 */ public function deleteCommentsAtObject($objectType, $objectId); /** * sets the read marker for a given file to the specified date for the * provided user * * @param string $objectType * @param string $objectId * @param \DateTime $dateTime * @param \OCP\IUser $user * @since 9.0.0 */ public function setReadMark($objectType, $objectId, \DateTime $dateTime, \OCP\IUser $user); /** * returns the read marker for a given file to the specified date for the * provided user. It returns null, when the marker is not present, i.e. * no comments were marked as read. * * @param string $objectType * @param string $objectId * @param \OCP\IUser $user * @return \DateTime|null * @since 9.0.0 */ public function getReadMark($objectType, $objectId, \OCP\IUser $user); /** * deletes the read markers for the specified user * * @param \OCP\IUser $user * @return bool * @since 9.0.0 */ public function deleteReadMarksFromUser(\OCP\IUser $user); /** * deletes the read markers on the specified object * * @param string $objectType * @param string $objectId * @return bool * @since 9.0.0 */ public function deleteReadMarksOnObject($objectType, $objectId); /** * registers an Entity to the manager, so event notifications can be send * to consumers of the comments infrastructure * * @param \Closure $closure * @since 11.0.0 */ public function registerEventHandler(\Closure $closure); /** * registers a method that resolves an ID to a display name for a given type * * @param string $type * @param \Closure $closure * @throws \OutOfBoundsException * @since 11.0.0 * * Only one resolver shall be registered per type. Otherwise a * \OutOfBoundsException has to thrown. */ public function registerDisplayNameResolver($type, \Closure $closure); /** * resolves a given ID of a given Type to a display name. * * @param string $type * @param string $id * @return string * @throws \OutOfBoundsException * @since 11.0.0 * * If a provided type was not registered, an \OutOfBoundsException shall * be thrown. It is upon the resolver discretion what to return of the * provided ID is unknown. It must be ensured that a string is returned. */ public function resolveDisplayName($type, $id); } public/Comments/ICommentsEventHandler.php 0000604 00000002153 15247130450 0014514 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Comments; /** * Interface ICommentsEventHandler * * @package OCP\Comments * @since 11.0.0 */ interface ICommentsEventHandler { /** * @param CommentsEvent $event * @since 11.0.0 */ public function handle(CommentsEvent $event); } public/Comments/IllegalIDChangeException.php 0000604 00000001712 15247130450 0015071 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Comments; /** * Exception for illegal attempts to modify a comment ID * @since 9.0.0 */ class IllegalIDChangeException extends \Exception {} public/Comments/MessageTooLongException.php 0000604 00000001664 15247130450 0015071 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Comments; /** * Exception thrown when a comment message exceeds the allowed character limit * @since 9.0.0 */ class MessageTooLongException extends \OverflowException {} public/Comments/CommentsEvent.php 0000604 00000003346 15247130450 0013112 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Comments; use Symfony\Component\EventDispatcher\Event; /** * Class CommentsEvent * * @package OCP\Comments * @since 9.0.0 */ class CommentsEvent extends Event { const EVENT_ADD = 'OCP\Comments\ICommentsManager::addComment'; const EVENT_PRE_UPDATE = 'OCP\Comments\ICommentsManager::preUpdateComment'; const EVENT_UPDATE = 'OCP\Comments\ICommentsManager::updateComment'; const EVENT_DELETE = 'OCP\Comments\ICommentsManager::deleteComment'; /** @var string */ protected $event; /** @var IComment */ protected $comment; /** * DispatcherEvent constructor. * * @param string $event * @param IComment $comment * @since 9.0.0 */ public function __construct($event, IComment $comment) { $this->event = $event; $this->comment = $comment; } /** * @return string * @since 9.0.0 */ public function getEvent() { return $this->event; } /** * @return IComment * @since 9.0.0 */ public function getComment() { return $this->comment; } } public/Comments/ICommentsManagerFactory.php 0000604 00000002730 15247130450 0015040 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Comments; use OCP\IServerContainer; /** * Interface ICommentsManagerFactory * * This class is responsible for instantiating and returning an ICommentsManager * instance. * * @package OCP\Comments * @since 9.0.0 */ interface ICommentsManagerFactory { /** * Constructor for the comments manager factory * * @param IServerContainer $serverContainer server container * @since 9.0.0 */ public function __construct(IServerContainer $serverContainer); /** * creates and returns an instance of the ICommentsManager * * @return ICommentsManager * @since 9.0.0 */ public function getManager(); } public/Image.php 0000604 00000002261 15247130450 0007553 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Image class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides functions to handle images * @since 6.0.0 */ class Image extends \OC_Image { } public/Share_Backend_Collection.php 0000604 00000002651 15247130450 0013360 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Interface for collections of of items implemented by another share backend. * Extends the Share_Backend interface. * @since 5.0.0 */ interface Share_Backend_Collection extends Share_Backend { /** * Get the sources of the children of the item * @param string $itemSource * @return array Returns an array of children each inside an array with the keys: source, target, and file_path if applicable * @since 5.0.0 */ public function getChildren($itemSource); } public/GroupInterface.php 0000604 00000006021 15247130450 0011444 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Group Class. * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * TODO actually this is a IGroupBackend * * @package OCP * @since 4.5.0 */ interface GroupInterface { /** * actions that user backends can define */ const CREATE_GROUP = 0x00000001; const DELETE_GROUP = 0x00000010; const ADD_TO_GROUP = 0x00000100; const REMOVE_FROM_GOUP = 0x00001000; // oops const REMOVE_FROM_GROUP = 0x00001000; //OBSOLETE const GET_DISPLAYNAME = 0x00010000; const COUNT_USERS = 0x00100000; const GROUP_DETAILS = 0x01000000; /** * Check if backend implements actions * @param int $actions bitwise-or'ed actions * @return boolean * @since 4.5.0 * * Returns the supported actions as int to be * compared with \OC_Group_Backend::CREATE_GROUP etc. */ public function implementsActions($actions); /** * is user in group? * @param string $uid uid of the user * @param string $gid gid of the group * @return bool * @since 4.5.0 * * Checks whether the user is member of a group or not. */ public function inGroup($uid, $gid); /** * Get all groups a user belongs to * @param string $uid Name of the user * @return array an array of group names * @since 4.5.0 * * This function fetches all groups a user belongs to. It does not check * if the user exists at all. */ public function getUserGroups($uid); /** * get a list of all groups * @param string $search * @param int $limit * @param int $offset * @return array an array of group names * @since 4.5.0 * * Returns a list with all groups */ public function getGroups($search = '', $limit = -1, $offset = 0); /** * check if a group exists * @param string $gid * @return bool * @since 4.5.0 */ public function groupExists($gid); /** * get a list of all users in a group * @param string $gid * @param string $search * @param int $limit * @param int $offset * @return array an array of user ids * @since 4.5.0 */ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0); } public/IDateTimeZone.php 0000604 00000002103 15247130450 0011165 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface IDateTimeZone * * @package OCP * @since 8.0.0 */ interface IDateTimeZone { /** * @param bool|int $timestamp * @return \DateTimeZone * @since 8.0.0 - parameter $timestamp was added in 8.1.0 */ public function getTimeZone($timestamp = false); } public/Lockdown/ILockdownManager.php 0000604 00000002311 15247130450 0013471 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Lockdown; use OC\Authentication\Token\IToken; /** * @since 9.2 */ interface ILockdownManager { /** * Enable the lockdown restrictions * * @since 9.2 */ public function enable(); /** * Set the active token to get the restrictions from and enable the lockdown * * @param IToken $token * @since 9.2 */ public function setToken(IToken $token); /** * Check whether or not filesystem access is allowed * * @return bool * @since 9.2 */ public function canAccessFilesystem(); } public/SabrePluginException.php 0000604 00000002005 15247130450 0012617 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; use Sabre\DAV\Exception; /** * @since 8.2.0 */ class SabrePluginException extends Exception { /** * Returns the HTTP statuscode for this exception * * @return int * @since 8.2.0 */ public function getHTTPCode() { return $this->code; } } public/Util.php 0000604 00000053434 15247130450 0007456 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Individual IT Services <info@individual-it.net> * @author Jens-Christian Fischer <jens-christian.fischer@switch.ch> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Nicolas Grekas <nicolas.grekas@gmail.com> * @author Pellaeon Lin <nfsmwlin@gmail.com> * @author Randolph Carter <RandolphCarter@fantasymail.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Herbrechtsmeier <stefan@herbrechtsmeier.net> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Utility Class. * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; use DateTimeZone; /** * This class provides different helper functions to make the life of a developer easier * @since 4.0.0 */ class Util { // consts for Logging const DEBUG=0; const INFO=1; const WARN=2; const ERROR=3; const FATAL=4; /** \OCP\Share\IManager */ private static $shareManager; /** * get the current installed version of ownCloud * @return array * @since 4.0.0 */ public static function getVersion() { return(\OC_Util::getVersion()); } /** * Set current update channel * @param string $channel * @since 8.1.0 */ public static function setChannel($channel) { \OC::$server->getConfig()->setSystemValue('updater.release.channel', $channel); } /** * Get current update channel * @return string * @since 8.1.0 */ public static function getChannel() { return \OC_Util::getChannel(); } /** * send an email * @param string $toaddress * @param string $toname * @param string $subject * @param string $mailtext * @param string $fromaddress * @param string $fromname * @param int $html * @param string $altbody * @param string $ccaddress * @param string $ccname * @param string $bcc * @deprecated 8.1.0 Use \OCP\Mail\IMailer instead * @since 4.0.0 */ public static function sendMail($toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, $html = 0, $altbody = '', $ccaddress = '', $ccname = '', $bcc = '') { $mailer = \OC::$server->getMailer(); $message = $mailer->createMessage(); $message->setTo([$toaddress => $toname]); $message->setSubject($subject); $message->setPlainBody($mailtext); $message->setFrom([$fromaddress => $fromname]); if($html === 1) { $message->setHTMLBody($altbody); } if($altbody === '') { $message->setHTMLBody($mailtext); $message->setPlainBody(''); } else { $message->setHtmlBody($mailtext); $message->setPlainBody($altbody); } if(!empty($ccaddress)) { if(!empty($ccname)) { $message->setCc([$ccaddress => $ccname]); } else { $message->setCc([$ccaddress]); } } if(!empty($bcc)) { $message->setBcc([$bcc]); } $mailer->send($message); } /** * write a message in the log * @param string $app * @param string $message * @param int $level * @since 4.0.0 */ public static function writeLog( $app, $message, $level ) { $context = ['app' => $app]; \OC::$server->getLogger()->log($level, $message, $context); } /** * write exception into the log * @param string $app app name * @param \Exception $ex exception to log * @param int $level log level, defaults to \OCP\Util::FATAL * @since ....0.0 - parameter $level was added in 7.0.0 * @deprecated 8.2.0 use logException of \OCP\ILogger */ public static function logException( $app, \Exception $ex, $level = \OCP\Util::FATAL ) { \OC::$server->getLogger()->logException($ex, ['app' => $app]); } /** * check if sharing is disabled for the current user * * @return boolean * @since 7.0.0 * @deprecated 9.1.0 Use \OC::$server->getShareManager()->sharingDisabledForUser */ public static function isSharingDisabledForUser() { if (self::$shareManager === null) { self::$shareManager = \OC::$server->getShareManager(); } $user = \OC::$server->getUserSession()->getUser(); if ($user !== null) { $user = $user->getUID(); } return self::$shareManager->sharingDisabledForUser($user); } /** * get l10n object * @param string $application * @param string|null $language * @return \OCP\IL10N * @since 6.0.0 - parameter $language was added in 8.0.0 */ public static function getL10N($application, $language = null) { return \OC::$server->getL10N($application, $language); } /** * add a css file * @param string $application * @param string $file * @since 4.0.0 */ public static function addStyle( $application, $file = null ) { \OC_Util::addStyle( $application, $file ); } /** * add a javascript file * @param string $application * @param string $file * @since 4.0.0 */ public static function addScript( $application, $file = null ) { \OC_Util::addScript( $application, $file ); } /** * Add a translation JS file * @param string $application application id * @param string $languageCode language code, defaults to the current locale * @since 8.0.0 */ public static function addTranslations($application, $languageCode = null) { \OC_Util::addTranslations($application, $languageCode); } /** * Add a custom element to the header * If $text is null then the element will be written as empty element. * So use "" to get a closing tag. * @param string $tag tag name of the element * @param array $attributes array of attributes for the element * @param string $text the text content for the element * @since 4.0.0 */ public static function addHeader($tag, $attributes, $text=null) { \OC_Util::addHeader($tag, $attributes, $text); } /** * formats a timestamp in the "right" way * @param int $timestamp $timestamp * @param bool $dateOnly option to omit time from the result * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to * @return string timestamp * * @deprecated 8.0.0 Use \OC::$server->query('DateTimeFormatter') instead * @since 4.0.0 */ public static function formatDate($timestamp, $dateOnly=false, $timeZone = null) { return(\OC_Util::formatDate($timestamp, $dateOnly, $timeZone)); } /** * check if some encrypted files are stored * @return bool * * @deprecated 8.1.0 No longer required * @since 6.0.0 */ public static function encryptedFiles() { return false; } /** * Creates an absolute url to the given app and file. * @param string $app app * @param string $file file * @param array $args array with param=>value, will be appended to the returned url * The value of $args will be urlencoded * @return string the url * @since 4.0.0 - parameter $args was added in 4.5.0 */ public static function linkToAbsolute( $app, $file, $args = array() ) { $urlGenerator = \OC::$server->getURLGenerator(); return $urlGenerator->getAbsoluteURL( $urlGenerator->linkTo($app, $file, $args) ); } /** * Creates an absolute url for remote use. * @param string $service id * @return string the url * @since 4.0.0 */ public static function linkToRemote( $service ) { $urlGenerator = \OC::$server->getURLGenerator(); $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; return $urlGenerator->getAbsoluteURL( $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') ); } /** * Creates an absolute url for public use * @param string $service id * @return string the url * @since 4.5.0 */ public static function linkToPublic($service) { return \OC_Helper::linkToPublic($service); } /** * Creates an url using a defined route * @param string $route * @param array $parameters * @internal param array $args with param=>value, will be appended to the returned url * @return string the url * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->linkToRoute($route, $parameters) * @since 5.0.0 */ public static function linkToRoute( $route, $parameters = array() ) { return \OC::$server->getURLGenerator()->linkToRoute($route, $parameters); } /** * Creates an url to the given app and file * @param string $app app * @param string $file file * @param array $args array with param=>value, will be appended to the returned url * The value of $args will be urlencoded * @return string the url * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->linkTo($app, $file, $args) * @since 4.0.0 - parameter $args was added in 4.5.0 */ public static function linkTo( $app, $file, $args = array() ) { return \OC::$server->getURLGenerator()->linkTo($app, $file, $args); } /** * Returns the server host, even if the website uses one or more reverse proxy * @return string the server host * @deprecated 8.1.0 Use \OCP\IRequest::getServerHost * @since 4.0.0 */ public static function getServerHost() { return \OC::$server->getRequest()->getServerHost(); } /** * Returns the server host name without an eventual port number * @return string the server hostname * @since 5.0.0 */ public static function getServerHostName() { $host_name = self::getServerHost(); // strip away port number (if existing) $colon_pos = strpos($host_name, ':'); if ($colon_pos != FALSE) { $host_name = substr($host_name, 0, $colon_pos); } return $host_name; } /** * Returns the default email address * @param string $user_part the user part of the address * @return string the default email address * * Assembles a default email address (using the server hostname * and the given user part, and returns it * Example: when given lostpassword-noreply as $user_part param, * and is currently accessed via http(s)://example.com/, * it would return 'lostpassword-noreply@example.com' * * If the configuration value 'mail_from_address' is set in * config.php, this value will override the $user_part that * is passed to this function * @since 5.0.0 */ public static function getDefaultEmailAddress($user_part) { $config = \OC::$server->getConfig(); $user_part = $config->getSystemValue('mail_from_address', $user_part); $host_name = self::getServerHostName(); $host_name = $config->getSystemValue('mail_domain', $host_name); $defaultEmailAddress = $user_part.'@'.$host_name; $mailer = \OC::$server->getMailer(); if ($mailer->validateMailAddress($defaultEmailAddress)) { return $defaultEmailAddress; } // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain' return $user_part.'@localhost.localdomain'; } /** * Returns the server protocol. It respects reverse proxy servers and load balancers * @return string the server protocol * @deprecated 8.1.0 Use \OCP\IRequest::getServerProtocol * @since 4.5.0 */ public static function getServerProtocol() { return \OC::$server->getRequest()->getServerProtocol(); } /** * Returns the request uri, even if the website uses one or more reverse proxies * @return string the request uri * @deprecated 8.1.0 Use \OCP\IRequest::getRequestUri * @since 5.0.0 */ public static function getRequestUri() { return \OC::$server->getRequest()->getRequestUri(); } /** * Returns the script name, even if the website uses one or more reverse proxies * @return string the script name * @deprecated 8.1.0 Use \OCP\IRequest::getScriptName * @since 5.0.0 */ public static function getScriptName() { return \OC::$server->getRequest()->getScriptName(); } /** * Creates path to an image * @param string $app app * @param string $image image name * @return string the url * @deprecated 8.1.0 Use \OC::$server->getURLGenerator()->imagePath($app, $image) * @since 4.0.0 */ public static function imagePath( $app, $image ) { return \OC::$server->getURLGenerator()->imagePath($app, $image); } /** * Make a human file size (2048 to 2 kB) * @param int $bytes file size in bytes * @return string a human readable file size * @since 4.0.0 */ public static function humanFileSize( $bytes ) { return(\OC_Helper::humanFileSize( $bytes )); } /** * Make a computer file size (2 kB to 2048) * @param string $str file size in a fancy format * @return int a file size in bytes * * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418 * @since 4.0.0 */ public static function computerFileSize( $str ) { return(\OC_Helper::computerFileSize( $str )); } /** * connects a function to a hook * * @param string $signalClass class name of emitter * @param string $signalName name of signal * @param string|object $slotClass class name of slot * @param string $slotName name of slot * @return bool * * This function makes it very easy to connect to use hooks. * * TODO: write example * @since 4.0.0 */ static public function connectHook($signalClass, $signalName, $slotClass, $slotName ) { return(\OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName )); } /** * Emits a signal. To get data from the slot use references! * @param string $signalclass class name of emitter * @param string $signalname name of signal * @param array $params default: array() array with additional data * @return bool true if slots exists or false if not * * TODO: write example * @since 4.0.0 */ static public function emitHook( $signalclass, $signalname, $params = array()) { return(\OC_Hook::emit( $signalclass, $signalname, $params )); } /** * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare * multiple OC_Template elements which invoke `callRegister`. If the value * would not be cached these unit-tests would fail. * @var string */ private static $token = ''; /** * Register an get/post call. This is important to prevent CSRF attacks * @since 4.5.0 */ public static function callRegister() { if(self::$token === '') { self::$token = \OC::$server->getCsrfTokenManager()->getToken()->getEncryptedValue(); } return self::$token; } /** * Check an ajax get/post call if the request token is valid. exit if not. * @since 4.5.0 * @deprecated 9.0.0 Use annotations based on the app framework. */ public static function callCheck() { if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { header('Location: '.\OC::$WEBROOT); exit(); } if (!(\OC::$server->getRequest()->passesCSRFCheck())) { exit(); } } /** * Used to sanitize HTML * * This function is used to sanitize HTML and should be applied on any * string or array of strings before displaying it on a web page. * * @param string|array $value * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter. * @since 4.5.0 */ public static function sanitizeHTML($value) { return \OC_Util::sanitizeHTML($value); } /** * Public function to encode url parameters * * This function is used to encode path to file before output. * Encoding is done according to RFC 3986 with one exception: * Character '/' is preserved as is. * * @param string $component part of URI to encode * @return string * @since 6.0.0 */ public static function encodePath($component) { return(\OC_Util::encodePath($component)); } /** * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. * * @param array $input The array to work on * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 * @return array * @since 4.5.0 */ public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { return(\OC_Helper::mb_array_change_key_case($input, $case, $encoding)); } /** * replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement. * * @param string $string The input string. Opposite to the PHP build-in function does not accept an array. * @param string $replacement The replacement string. * @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. * @param int $length Length of the part to be replaced * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 * @return string * @since 4.5.0 * @deprecated 8.2.0 Use substr_replace() instead. */ public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') { return substr_replace($string, $replacement, $start, $length); } /** * Replace all occurrences of the search string with the replacement string * * @param string $search The value being searched for, otherwise known as the needle. String. * @param string $replace The replacement string. * @param string $subject The string or array being searched and replaced on, otherwise known as the haystack. * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 * @param int $count If passed, this will be set to the number of replacements performed. * @return string * @since 4.5.0 * @deprecated 8.2.0 Use str_replace() instead. */ public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) { return str_replace($search, $replace, $subject, $count); } /** * performs a search in a nested array * * @param array $haystack the array to be searched * @param string $needle the search string * @param int $index optional, only search this key name * @return mixed the key of the matching field, otherwise false * @since 4.5.0 */ public static function recursiveArraySearch($haystack, $needle, $index = null) { return(\OC_Helper::recursiveArraySearch($haystack, $needle, $index)); } /** * calculates the maximum upload size respecting system settings, free space and user quota * * @param string $dir the current folder where the user currently operates * @param int $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly * @return int number of bytes representing * @since 5.0.0 */ public static function maxUploadFilesize($dir, $free = null) { return \OC_Helper::maxUploadFilesize($dir, $free); } /** * Calculate free space left within user quota * @param string $dir the current folder where the user currently operates * @return int number of bytes representing * @since 7.0.0 */ public static function freeSpace($dir) { return \OC_Helper::freeSpace($dir); } /** * Calculate PHP upload limit * * @return int number of bytes representing * @since 7.0.0 */ public static function uploadLimit() { return \OC_Helper::uploadLimit(); } /** * Returns whether the given file name is valid * @param string $file file name to check * @return bool true if the file name is valid, false otherwise * @deprecated 8.1.0 use \OC\Files\View::verifyPath() * @since 7.0.0 */ public static function isValidFileName($file) { return \OC_Util::isValidFileName($file); } /** * Generates a cryptographic secure pseudo-random string * @param int $length of the random string * @return string * @deprecated 8.0.0 Use \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($length); instead * @since 7.0.0 */ public static function generateRandomBytes($length = 30) { return \OC::$server->getSecureRandom()->generate($length, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS); } /** * Compare two strings to provide a natural sort * @param string $a first string to compare * @param string $b second string to compare * @return -1 if $b comes before $a, 1 if $a comes before $b * or 0 if the strings are identical * @since 7.0.0 */ public static function naturalSortCompare($a, $b) { return \OC\NaturalSort::getInstance()->compare($a, $b); } /** * check if a password is required for each public link * @return boolean * @since 7.0.0 */ public static function isPublicLinkPasswordRequired() { return \OC_Util::isPublicLinkPasswordRequired(); } /** * check if share API enforces a default expire date * @return boolean * @since 8.0.0 */ public static function isDefaultExpireDateEnforced() { return \OC_Util::isDefaultExpireDateEnforced(); } protected static $needUpgradeCache = null; /** * Checks whether the current version needs upgrade. * * @return bool true if upgrade is needed, false otherwise * @since 7.0.0 */ public static function needUpgrade() { if (!isset(self::$needUpgradeCache)) { self::$needUpgradeCache=\OC_Util::needUpgrade(\OC::$server->getSystemConfig()); } return self::$needUpgradeCache; } } public/IL10N.php 0000604 00000006531 15247130450 0007320 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * L10n interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Interface IL10N * * @package OCP * @since 6.0.0 */ interface IL10N { /** * Translating * @param string $text The text we need a translation for * @param array $parameters default:array() Parameters for sprintf * @return \OC_L10N_String Translation or the same text * * Returns the translation. If no translation is found, $text will be * returned. * @since 6.0.0 */ public function t($text, $parameters = array()); /** * Translating * @param string $text_singular the string to translate for exactly one object * @param string $text_plural the string to translate for n objects * @param integer $count Number of objects * @param array $parameters default:array() Parameters for sprintf * @return \OC_L10N_String Translation or the same text * * Returns the translation. If no translation is found, $text will be * returned. %n will be replaced with the number of objects. * * The correct plural is determined by the plural_forms-function * provided by the po file. * @since 6.0.0 * */ public function n($text_singular, $text_plural, $count, $parameters = array()); /** * Localization * @param string $type Type of localization * @param \DateTime|int|string $data parameters for this localization * @param array $options currently supports following options: * - 'width': handed into \Punic\Calendar::formatDate as second parameter * @return string|int|false * * Returns the localized data. * * Implemented types: * - date * - Creates a date * - l10n-field: date * - params: timestamp (int/string) * - datetime * - Creates date and time * - l10n-field: datetime * - params: timestamp (int/string) * - time * - Creates a time * - l10n-field: time * - params: timestamp (int/string) * @since 6.0.0 - parameter $options was added in 8.0.0 */ public function l($type, $data, $options = array()); /** * The code (en, de, ...) of the language that is used for this IL10N object * * @return string language * @since 7.0.0 */ public function getLanguageCode(); } public/ICertificateManager.php 0000604 00000004073 15247130450 0012362 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Manage trusted certificates for users * @since 8.0.0 */ interface ICertificateManager { /** * Returns all certificates trusted by the user * * @return \OCP\ICertificate[] * @since 8.0.0 */ public function listCertificates(); /** * @param string $certificate the certificate data * @param string $name the filename for the certificate * @return \OCP\ICertificate * @throws \Exception If the certificate could not get added * @since 8.0.0 - since 8.1.0 throws exception instead of returning false */ public function addCertificate($certificate, $name); /** * @param string $name * @since 8.0.0 */ public function removeCertificate($name); /** * Get the path to the certificate bundle for this user * * @param string $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle (since 9.0.0) * @return string * @since 8.0.0 */ public function getCertificateBundle($uid = ''); /** * Get the full local path to the certificate bundle for this user * * @param string $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle * @return string * @since 9.0.0 */ public function getAbsoluteBundlePath($uid = ''); } public/IHelper.php 0000604 00000003007 15247130450 0010060 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Helper interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Functions that don't have any specific interface to place * @since 6.0.0 * @deprecated 8.1.0 */ interface IHelper { /** * Gets the content of an URL by using CURL or a fallback if it is not * installed * @param string $url the url that should be fetched * @return string the content of the webpage * @since 6.0.0 * @deprecated 8.1.0 Use \OCP\IServerContainer::getHTTPClientService */ public function getUrlContent($url); } public/IMemcacheTTL.php 0000604 00000002166 15247130450 0010734 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface for memcache backends that support setting ttl after the value is set * * @since 8.2.2 */ interface IMemcacheTTL extends IMemcache { /** * Set the ttl for an existing value * * @param string $key * @param int $ttl time to live in seconds * @since 8.2.2 */ public function setTTL($key, $ttl); } public/IAddressBook.php 0000604 00000005562 15247130450 0011051 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * IAddressBook interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP { /** * Interface IAddressBook * * @package OCP * @since 5.0.0 */ interface IAddressBook { /** * @return string defining the technical unique key * @since 5.0.0 */ public function getKey(); /** * In comparison to getKey() this function returns a human readable (maybe translated) name * @return mixed * @since 5.0.0 */ public function getDisplayName(); /** * @param string $pattern which should match within the $searchProperties * @param array $searchProperties defines the properties within the query pattern should match * @param array $options - for future use. One should always have options! * @return array an array of contacts which are arrays of key-value-pairs * @since 5.0.0 */ public function search($pattern, $searchProperties, $options); // // dummy results // return array( // array('id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => 'a@b.c', 'GEO' => '37.386013;-122.082932'), // array('id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => array('d@e.f', 'g@h.i')), // ); /** * @param array $properties this array if key-value-pairs defines a contact * @return array an array representing the contact just created or updated * @since 5.0.0 */ public function createOrUpdate($properties); // // dummy // return array('id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => 'a@b.c', // 'PHOTO' => 'VALUE=uri:http://www.abc.com/pub/photos/jqpublic.gif', // 'ADR' => ';;123 Main Street;Any Town;CA;91921-1234' // ); /** * @return mixed * @since 5.0.0 */ public function getPermissions(); /** * @param object $id the unique identifier to a contact * @return bool successful or not * @since 5.0.0 */ public function delete($id); } } public/Template.php 0000604 00000015006 15247130450 0010305 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Template Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Make OC_Helper::imagePath available as a simple function * @param string $app * @param string $image * @return string to the image * * @see \OCP\IURLGenerator::imagePath * @deprecated 8.0.0 Use \OCP\Template::image_path() instead */ function image_path( $app, $image ) { return(\image_path( $app, $image )); } /** * Make OC_Helper::mimetypeIcon available as a simple function * @param string $mimetype * @return string to the image of this file type. * @deprecated 8.0.0 Use \OCP\Template::mimetype_icon() instead */ function mimetype_icon( $mimetype ) { return(\mimetype_icon( $mimetype )); } /** * Make preview_icon available as a simple function * @param string $path path to file * @return string to the preview of the image * @deprecated 8.0.0 Use \OCP\Template::preview_icon() instead */ function preview_icon( $path ) { return(\preview_icon( $path )); } /** * Make publicpreview_icon available as a simple function * Returns the path to the preview of the image. * @param string $path of file * @param string $token * @return string link to the preview * @deprecated 8.0.0 Use \OCP\Template::publicPreview_icon() instead */ function publicPreview_icon ( $path, $token ) { return(\publicPreview_icon( $path, $token )); } /** * Make OC_Helper::humanFileSize available as a simple function * Example: 2048 to 2 kB. * @param int $bytes in bytes * @return string size as string * @deprecated 8.0.0 Use \OCP\Template::human_file_size() instead */ function human_file_size( $bytes ) { return(\human_file_size( $bytes )); } /** * Return the relative date in relation to today. Returns something like "last hour" or "two month ago" * @param int $timestamp unix timestamp * @param boolean $dateOnly * @return \OC_L10N_String human readable interpretation of the timestamp * * @deprecated 8.0.0 Use \OCP\Template::relative_modified_date() instead */ function relative_modified_date( $timestamp, $dateOnly = false ) { return(\relative_modified_date($timestamp, null, $dateOnly)); } /** * Return a human readable outout for a file size. * @param integer $bytes size of a file in byte * @return string human readable interpretation of a file size * @deprecated 8.0.0 Use \OCP\Template::human_file_size() instead */ function simple_file_size($bytes) { return(\human_file_size($bytes)); } /** * Generate html code for an options block. * @param array $options the options * @param mixed $selected which one is selected? * @param array $params the parameters * @return string html options * @deprecated 8.0.0 Use \OCP\Template::html_select_options() instead */ function html_select_options($options, $selected, $params=array()) { return(\html_select_options($options, $selected, $params)); } /** * This class provides the template system for owncloud. You can use it to load * specific templates, add data and generate the html code * * @since 8.0.0 */ class Template extends \OC_Template { /** * Make OC_Helper::imagePath available as a simple function * * @see \OCP\IURLGenerator::imagePath * * @param string $app * @param string $image * @return string to the image * @since 8.0.0 */ public static function image_path($app, $image) { return \image_path($app, $image); } /** * Make OC_Helper::mimetypeIcon available as a simple function * * @param string $mimetype * @return string to the image of this file type. * @since 8.0.0 */ public static function mimetype_icon($mimetype) { return \mimetype_icon($mimetype); } /** * Make preview_icon available as a simple function * * @param string $path path to file * @return string to the preview of the image * @since 8.0.0 */ public static function preview_icon($path) { return \preview_icon($path); } /** * Make publicpreview_icon available as a simple function * Returns the path to the preview of the image. * * @param string $path of file * @param string $token * @return string link to the preview * @since 8.0.0 */ public static function publicPreview_icon($path, $token) { return \publicPreview_icon($path, $token); } /** * Make OC_Helper::humanFileSize available as a simple function * Example: 2048 to 2 kB. * * @param int $bytes in bytes * @return string size as string * @since 8.0.0 */ public static function human_file_size($bytes) { return \human_file_size($bytes); } /** * Return the relative date in relation to today. Returns something like "last hour" or "two month ago" * * @param int $timestamp unix timestamp * @param boolean $dateOnly * @return string human readable interpretation of the timestamp * @since 8.0.0 */ public static function relative_modified_date($timestamp, $dateOnly = false) { return \relative_modified_date($timestamp, null, $dateOnly); } /** * Generate html code for an options block. * * @param array $options the options * @param mixed $selected which one is selected? * @param array $params the parameters * @return string html options * @since 8.0.0 */ public static function html_select_options($options, $selected, $params=array()) { return \html_select_options($options, $selected, $params); } } public/ILogger.php 0000604 00000006570 15247130450 0010070 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface ILogger * @package OCP * @since 7.0.0 * * This logger interface follows the design guidelines of PSR-3 * https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#3-psrlogloggerinterface */ interface ILogger { /** * System is unusable. * * @param string $message * @param array $context * @return null * @since 7.0.0 */ public function emergency($message, array $context = array()); /** * Action must be taken immediately. * * @param string $message * @param array $context * @return null * @since 7.0.0 */ public function alert($message, array $context = array()); /** * Critical conditions. * * @param string $message * @param array $context * @return null * @since 7.0.0 */ public function critical($message, array $context = array()); /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message * @param array $context * @return null * @since 7.0.0 */ public function error($message, array $context = array()); /** * Exceptional occurrences that are not errors. * * @param string $message * @param array $context * @return null * @since 7.0.0 */ public function warning($message, array $context = array()); /** * Normal but significant events. * * @param string $message * @param array $context * @return null * @since 7.0.0 */ public function notice($message, array $context = array()); /** * Interesting events. * * @param string $message * @param array $context * @return null * @since 7.0.0 */ public function info($message, array $context = array()); /** * Detailed debug information. * * @param string $message * @param array $context * @return null * @since 7.0.0 */ public function debug($message, array $context = array()); /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context * @return mixed * @since 7.0.0 */ public function log($level, $message, array $context = array()); /** * Logs an exception very detailed * An additional message can we written to the log by adding it to the * context. * * <code> * $logger->logException($ex, [ * 'message' => 'Exception during background job execution' * ]); * </code> * * @param \Exception | \Throwable $exception * @param array $context * @return void * @since 8.2.0 */ public function logException($exception, array $context = array()); } public/Share/Exceptions/ShareNotFound.php 0000604 00000001643 15247130450 0014436 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Share\Exceptions; /** * Class ShareNotFound * * @package OCP\Share\Exceptions * @since 9.0.0 */ class ShareNotFound extends GenericShareException { } public/Share/Exceptions/IllegalIDChangeException.php 0000604 00000001655 15247130450 0016475 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Share\Exceptions; /** * Exception for illegal attempts to modify an id of a share * @since 9.1.0 */ class IllegalIDChangeException extends GenericShareException {} public/Share/Exceptions/GenericShareException.php 0000604 00000002541 15247130450 0016133 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Share\Exceptions; use OC\HintException; /** * Class GenericEncryptionException * * @package OCP\Share\Exceptions * @since 9.0.0 */ class GenericShareException extends HintException { /** * @param string $message * @param string $hint * @param int $code * @param \Exception $previous * @since 9.0.0 */ public function __construct($message = '', $hint = '', $code = 0, \Exception $previous = null) { if (empty($message)) { $message = 'Unspecified share exception'; } parent::__construct($message, $hint, $code, $previous); } } public/Share/IShareHelper.php 0000604 00000002273 15247130450 0012111 0 ustar 00 <?php /** * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Share; use OCP\Files\Node; /** * Interface IShareHelper * * @package OCP\Share * @since 12 */ interface IShareHelper { /** * @param Node $node * @return array [ users => [Mapping $uid => $pathForUser], remotes => [Mapping $cloudId => $pathToMountRoot]] * @since 12 */ public function getPathsForAccessList(Node $node); } public/Share/IShare.php 0000604 00000016214 15247130450 0010751 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Share; use OCP\Files\Cache\ICacheEntry; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Share\Exceptions\IllegalIDChangeException; /** * Interface IShare * * @package OCP\Share * @since 9.0.0 */ interface IShare { /** * Set the internal id of the share * It is only allowed to set the internal id of a share once. * Attempts to override the internal id will result in an IllegalIDChangeException * * @param string $id * @return \OCP\Share\IShare * @throws IllegalIDChangeException * @throws \InvalidArgumentException * @since 9.1.0 */ public function setId($id); /** * Get the internal id of the share. * * @return string * @since 9.0.0 */ public function getId(); /** * Get the full share id. This is the <providerid>:<internalid>. * The full id is unique in the system. * * @return string * @since 9.0.0 * @throws \UnexpectedValueException If the fullId could not be constructed */ public function getFullId(); /** * Set the provider id of the share * It is only allowed to set the provider id of a share once. * Attempts to override the provider id will result in an IllegalIDChangeException * * @param string $id * @return \OCP\Share\IShare * @throws IllegalIDChangeException * @throws \InvalidArgumentException * @since 9.1.0 */ public function setProviderId($id); /** * Set the node of the file/folder that is shared * * @param Node $node * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setNode(Node $node); /** * Get the node of the file/folder that is shared * * @return File|Folder * @since 9.0.0 * @throws NotFoundException */ public function getNode(); /** * Set file id for lazy evaluation of the node * @param int $fileId * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setNodeId($fileId); /** * Get the fileid of the node of this share * @return int * @since 9.0.0 * @throws NotFoundException */ public function getNodeId(); /** * Set the type of node (file/folder) * * @param string $type * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setNodeType($type); /** * Get the type of node (file/folder) * * @return string * @since 9.0.0 * @throws NotFoundException */ public function getNodeType(); /** * Set the shareType * * @param int $shareType * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setShareType($shareType); /** * Get the shareType * * @return int * @since 9.0.0 */ public function getShareType(); /** * Set the receiver of this share. * * @param string $sharedWith * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setSharedWith($sharedWith); /** * Get the receiver of this share. * * @return string * @since 9.0.0 */ public function getSharedWith(); /** * Set the permissions. * See \OCP\Constants::PERMISSION_* * * @param int $permissions * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setPermissions($permissions); /** * Get the share permissions * See \OCP\Constants::PERMISSION_* * * @return int * @since 9.0.0 */ public function getPermissions(); /** * Set the expiration date * * @param null|\DateTime $expireDate * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setExpirationDate($expireDate); /** * Get the expiration date * * @return \DateTime * @since 9.0.0 */ public function getExpirationDate(); /** * Set the sharer of the path. * * @param string $sharedBy * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setSharedBy($sharedBy); /** * Get share sharer * * @return string * @since 9.0.0 */ public function getSharedBy(); /** * Set the original share owner (who owns the path that is shared) * * @param string $shareOwner * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setShareOwner($shareOwner); /** * Get the original share owner (who owns the path that is shared) * * @return string * @since 9.0.0 */ public function getShareOwner(); /** * Set the password for this share. * When the share is passed to the share manager to be created * or updated the password will be hashed. * * @param string $password * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setPassword($password); /** * Get the password of this share. * If this share is obtained via a shareprovider the password is * hashed. * * @return string * @since 9.0.0 */ public function getPassword(); /** * Set the public link token. * * @param string $token * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setToken($token); /** * Get the public link token. * * @return string * @since 9.0.0 */ public function getToken(); /** * Set the target path of this share relative to the recipients user folder. * * @param string $target * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setTarget($target); /** * Get the target path of this share relative to the recipients user folder. * * @return string * @since 9.0.0 */ public function getTarget(); /** * Set the time this share was created * * @param \DateTime $shareTime * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setShareTime(\DateTime $shareTime); /** * Get the timestamp this share was created * * @return \DateTime * @since 9.0.0 */ public function getShareTime(); /** * Set if the recipient is informed by mail about the share. * * @param bool $mailSend * @return \OCP\Share\IShare The modified object * @since 9.0.0 */ public function setMailSend($mailSend); /** * Get if the recipient informed by mail about the share. * * @return bool * @since 9.0.0 */ public function getMailSend(); /** * Set the cache entry for the shared node * * @param ICacheEntry $entry * @since 11.0.0 */ public function setNodeCacheEntry(ICacheEntry $entry); /** * Get the cache entry for the shared node * * @return null|ICacheEntry * @since 11.0.0 */ public function getNodeCacheEntry(); } public/Share/IManager.php 0000604 00000020057 15247130450 0011261 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Share; use OCP\Files\Folder; use OCP\Files\Node; use OCP\Share\Exceptions\ShareNotFound; /** * Interface IManager * * @package OCP\Share * @since 9.0.0 */ interface IManager { /** * Create a Share * * @param IShare $share * @return IShare The share object * @since 9.0.0 */ public function createShare(IShare $share); /** * Update a share. * The target of the share can't be changed this way: use moveShare * The share can't be removed this way (permission 0): use deleteShare * * @param IShare $share * @return IShare The share object * @since 9.0.0 */ public function updateShare(IShare $share); /** * Delete a share * * @param IShare $share * @throws ShareNotFound * @since 9.0.0 */ public function deleteShare(IShare $share); /** * Unshare a file as the recipient. * This can be different from a regular delete for example when one of * the users in a groups deletes that share. But the provider should * handle this. * * @param IShare $share * @param string $recipientId * @since 9.0.0 */ public function deleteFromSelf(IShare $share, $recipientId); /** * Move the share as a recipient of the share. * This is updating the share target. So where the recipient has the share mounted. * * @param IShare $share * @param string $recipientId * @return IShare * @throws \InvalidArgumentException If $share is a link share or the $recipient does not match * @since 9.0.0 */ public function moveShare(IShare $share, $recipientId); /** * Get all shares shared by (initiated) by the provided user in a folder. * * @param string $userId * @param Folder $node * @param bool $reshares * @return IShare[][] [$fileId => IShare[], ...] * @since 11.0.0 */ public function getSharesInFolder($userId, Folder $node, $reshares = false); /** * Get shares shared by (initiated) by the provided user. * * @param string $userId * @param int $shareType * @param Node|null $path * @param bool $reshares * @param int $limit The maximum number of returned results, -1 for all results * @param int $offset * @return IShare[] * @since 9.0.0 */ public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0); /** * Get shares shared with $user. * Filter by $node if provided * * @param string $userId * @param int $shareType * @param Node|null $node * @param int $limit The maximum number of shares returned, -1 for all * @param int $offset * @return IShare[] * @since 9.0.0 */ public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0); /** * Retrieve a share by the share id. * If the recipient is set make sure to retrieve the file for that user. * This makes sure that if a user has moved/deleted a group share this * is reflected. * * @param string $id * @param string|null $recipient userID of the recipient * @return IShare * @throws ShareNotFound * @since 9.0.0 */ public function getShareById($id, $recipient = null); /** * Get the share by token possible with password * * @param string $token * @return IShare * @throws ShareNotFound * @since 9.0.0 */ public function getShareByToken($token); /** * Verify the password of a public share * * @param IShare $share * @param string $password * @return bool * @since 9.0.0 */ public function checkPassword(IShare $share, $password); /** * The user with UID is deleted. * All share providers have to cleanup the shares with this user as well * as shares owned by this user. * Shares only initiated by this user are fine. * * @param string $uid * @since 9.1.0 */ public function userDeleted($uid); /** * The group with $gid is deleted * We need to clear up all shares to this group * * @param string $gid * @since 9.1.0 */ public function groupDeleted($gid); /** * The user $uid is deleted from the group $gid * All user specific group shares have to be removed * * @param string $uid * @param string $gid * @since 9.1.0 */ public function userDeletedFromGroup($uid, $gid); /** * Get access list to a path. This means * all the users that can access a given path. * * Consider: * -root * |-folder1 (23) * |-folder2 (32) * |-fileA (42) * * fileA is shared with user1 and user1@server1 * folder2 is shared with group2 (user4 is a member of group2) * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2 * * Then the access list to '/folder1/folder2/fileA' with $currentAccess is: * [ * users => [ * 'user1' => ['node_id' => 42, 'node_path' => '/fileA'], * 'user4' => ['node_id' => 32, 'node_path' => '/folder2'], * 'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'], * ], * remote => [ * 'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'], * 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'], * ], * public => bool * mail => bool * ] * * The access list to '/folder1/folder2/fileA' **without** $currentAccess is: * [ * users => ['user1', 'user2', 'user4'], * remote => bool, * public => bool * mail => bool * ] * * This is required for encryption/activity * * @param \OCP\Files\Node $path * @param bool $recursive Should we check all parent folders as well * @param bool $currentAccess Should the user have currently access to the file * @return array * @since 12 */ public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false); /** * Instantiates a new share object. This is to be passed to * createShare. * * @return IShare * @since 9.0.0 */ public function newShare(); /** * Is the share API enabled * * @return bool * @since 9.0.0 */ public function shareApiEnabled(); /** * Is public link sharing enabled * * @return bool * @since 9.0.0 */ public function shareApiAllowLinks(); /** * Is password on public link requires * * @return bool * @since 9.0.0 */ public function shareApiLinkEnforcePassword(); /** * Is default expire date enabled * * @return bool * @since 9.0.0 */ public function shareApiLinkDefaultExpireDate(); /** * Is default expire date enforced *` * @return bool * @since 9.0.0 */ public function shareApiLinkDefaultExpireDateEnforced(); /** * Number of default expire days * * @return int * @since 9.0.0 */ public function shareApiLinkDefaultExpireDays(); /** * Allow public upload on link shares * * @return bool * @since 9.0.0 */ public function shareApiLinkAllowPublicUpload(); /** * check if user can only share with group members * @return bool * @since 9.0.0 */ public function shareWithGroupMembersOnly(); /** * Check if users can share with groups * @return bool * @since 9.0.1 */ public function allowGroupSharing(); /** * Check if sharing is disabled for the given user * * @param string $userId * @return bool * @since 9.0.0 */ public function sharingDisabledForUser($userId); /** * Check if outgoing server2server shares are allowed * @return bool * @since 9.0.0 */ public function outgoingServer2ServerSharesAllowed(); /** * Check if a given share provider exists * @param int $shareType * @return bool * @since 11.0.0 */ public function shareProviderExists($shareType); } public/Share/IProviderFactory.php 0000604 00000002774 15247130450 0013037 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Share; use OC\Share20\Exception\ProviderException; use OCP\IServerContainer; /** * Interface IProviderFactory * * @package OC\Share20 * @since 9.0.0 */ interface IProviderFactory { /** * IProviderFactory constructor. * @param IServerContainer $serverContainer * @since 9.0.0 */ public function __construct(IServerContainer $serverContainer); /** * @param string $id * @return IShareProvider * @throws ProviderException * @since 9.0.0 */ public function getProvider($id); /** * @param int $shareType * @return IShareProvider * @throws ProviderException * @since 9.0.0 */ public function getProviderForType($shareType); /** * @return IShareProvider[] * @since 11.0.0 */ public function getAllProviders(); } public/Share/IShareProvider.php 0000604 00000012416 15247130450 0012464 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Share; use OCP\Files\Folder; use OCP\Share\Exceptions\ShareNotFound; use OCP\Files\Node; /** * Interface IShareProvider * * @package OCP\Share * @since 9.0.0 */ interface IShareProvider { /** * Return the identifier of this provider. * * @return string Containing only [a-zA-Z0-9] * @since 9.0.0 */ public function identifier(); /** * Create a share * * @param \OCP\Share\IShare $share * @return \OCP\Share\IShare The share object * @since 9.0.0 */ public function create(\OCP\Share\IShare $share); /** * Update a share * * @param \OCP\Share\IShare $share * @return \OCP\Share\IShare The share object * @since 9.0.0 */ public function update(\OCP\Share\IShare $share); /** * Delete a share * * @param \OCP\Share\IShare $share * @since 9.0.0 */ public function delete(\OCP\Share\IShare $share); /** * Unshare a file from self as recipient. * This may require special handling. If a user unshares a group * share from their self then the original group share should still exist. * * @param \OCP\Share\IShare $share * @param string $recipient UserId of the recipient * @since 9.0.0 */ public function deleteFromSelf(\OCP\Share\IShare $share, $recipient); /** * Move a share as a recipient. * This is updating the share target. Thus the mount point of the recipient. * This may require special handling. If a user moves a group share * the target should only be changed for them. * * @param \OCP\Share\IShare $share * @param string $recipient userId of recipient * @return \OCP\Share\IShare * @since 9.0.0 */ public function move(\OCP\Share\IShare $share, $recipient); /** * Get all shares by the given user in a folder * * @param string $userId * @param Folder $node * @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator * @return \OCP\Share\IShare[] * @since 11.0.0 */ public function getSharesInFolder($userId, Folder $node, $reshares); /** * Get all shares by the given user * * @param string $userId * @param int $shareType * @param Node|null $node * @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator * @param int $limit The maximum number of shares to be returned, -1 for all shares * @param int $offset * @return \OCP\Share\IShare[] * @since 9.0.0 */ public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset); /** * Get share by id * * @param int $id * @param string|null $recipientId * @return \OCP\Share\IShare * @throws ShareNotFound * @since 9.0.0 */ public function getShareById($id, $recipientId = null); /** * Get shares for a given path * * @param Node $path * @return \OCP\Share\IShare[] * @since 9.0.0 */ public function getSharesByPath(Node $path); /** * Get shared with the given user * * @param string $userId get shares where this user is the recipient * @param int $shareType * @param Node|null $node * @param int $limit The max number of entries returned, -1 for all * @param int $offset * @return \OCP\Share\IShare[] * @since 9.0.0 */ public function getSharedWith($userId, $shareType, $node, $limit, $offset); /** * Get a share by token * * @param string $token * @return \OCP\Share\IShare * @throws ShareNotFound * @since 9.0.0 */ public function getShareByToken($token); /** * A user is deleted from the system * So clean up the relevant shares. * * @param string $uid * @param int $shareType * @since 9.1.0 */ public function userDeleted($uid, $shareType); /** * A group is deleted from the system. * We have to clean up all shares to this group. * Providers not handling group shares should just return * * @param string $gid * @since 9.1.0 */ public function groupDeleted($gid); /** * A user is deleted from a group * We have to clean up all the related user specific group shares * Providers not handling group shares should just return * * @param string $uid * @param string $gid * @since 9.1.0 */ public function userDeletedFromGroup($uid, $gid); /** * Get the access list to the array of provided nodes. * * @see IManager::getAccessList() for sample docs * * @param Node[] $nodes The list of nodes to get access for * @param bool $currentAccess If current access is required (like for removed shares that might get revived later) * @return array * @since 12 */ public function getAccessList($nodes, $currentAccess); } public/AppFramework/OCS/OCSForbiddenException.php 0000604 00000002414 15247130450 0015673 0 ustar 00 <?php /** * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\AppFramework\OCS; use Exception; use OCP\AppFramework\Http; /** * Class OCSForbiddenException * * @package OCP\AppFramework * @since 9.1.0 */ class OCSForbiddenException extends OCSException { /** * OCSForbiddenException constructor. * * @param string $message * @param Exception|null $previous * @since 9.1.0 */ public function __construct($message = '', Exception $previous = null) { parent::__construct($message, Http::STATUS_FORBIDDEN, $previous); } } public/AppFramework/OCS/OCSNotFoundException.php 0000604 00000002411 15247130450 0015530 0 ustar 00 <?php /** * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\AppFramework\OCS; use Exception; use OCP\AppFramework\Http; /** * Class OCSNotFoundException * * @package OCP\AppFramework * @since 9.1.0 */ class OCSNotFoundException extends OCSException { /** * OCSNotFoundException constructor. * * @param string $message * @param Exception|null $previous * @since 9.1.0 */ public function __construct($message = '', Exception $previous = null) { parent::__construct($message, Http::STATUS_NOT_FOUND, $previous); } } public/AppFramework/OCS/OCSException.php 0000604 00000001705 15247130450 0014060 0 ustar 00 <?php /** * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\AppFramework\OCS; use Exception; /** * Class OCSException * * @package OCP\AppFramework * @since 9.1.0 */ class OCSException extends Exception {} public/AppFramework/OCS/OCSBadRequestException.php 0000604 00000002422 15247130450 0016035 0 ustar 00 <?php /** * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\AppFramework\OCS; use Exception; use OCP\AppFramework\Http; /** * Class OCSBadRequestException * * @package OCP\AppFramework * @since 9.1.0 */ class OCSBadRequestException extends OCSException { /** * OCSBadRequestException constructor. * * @param string $message * @param Exception|null $previous * @since 9.1.0 */ public function __construct($message = '', Exception $previous = null) { parent::__construct($message, Http::STATUS_BAD_REQUEST, $previous); } } public/AppFramework/IApi.php 0000604 00000005617 15247130450 0011761 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework/IApi interface */ namespace OCP\AppFramework; /** * A few very basic and frequently used API functions are combined in here * @deprecated 8.0.0 */ interface IApi { /** * Gets the userid of the current user * @return string the user id of the current user * @deprecated 8.0.0 Use \OC::$server->getUserSession()->getUser()->getUID() */ function getUserId(); /** * Adds a new javascript file * @deprecated 8.0.0 include javascript and css in template files * @param string $scriptName the name of the javascript in js/ without the suffix * @param string $appName the name of the app, defaults to the current one * @return void */ function addScript($scriptName, $appName = null); /** * Adds a new css file * @deprecated 8.0.0 include javascript and css in template files * @param string $styleName the name of the css file in css/without the suffix * @param string $appName the name of the app, defaults to the current one * @return void */ function addStyle($styleName, $appName = null); /** * @deprecated 8.0.0 include javascript and css in template files * shorthand for addScript for files in the 3rdparty directory * @param string $name the name of the file without the suffix * @return void */ function add3rdPartyScript($name); /** * @deprecated 8.0.0 include javascript and css in template files * shorthand for addStyle for files in the 3rdparty directory * @param string $name the name of the file without the suffix * @return void */ function add3rdPartyStyle($name); /** * Checks if an app is enabled * @deprecated 8.0.0 communication between apps should happen over built in * callbacks or interfaces (check the contacts and calendar managers) * Checks if an app is enabled * also use \OC::$server->getAppManager()->isEnabledForUser($appName) * @param string $appName the name of an app * @return bool true if app is enabled */ public function isAppEnabled($appName); } public/AppFramework/QueryException.php 0000604 00000001717 15247130450 0014120 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework; use Exception; /** * Class QueryException * * @package OCP\AppFramework * @since 8.1.0 */ class QueryException extends Exception {} public/AppFramework/Http/OCSResponse.php 0000604 00000005076 15247130450 0014220 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\HTTP\JSONResponse class */ namespace OCP\AppFramework\Http; /** * A renderer for OCS responses * @since 8.1.0 * @deprecated 9.2.0 To implement an OCS endpoint extend the OCSController */ class OCSResponse extends Response { private $data; private $format; private $statuscode; private $message; private $itemscount; private $itemsperpage; /** * generates the xml or json response for the API call from an multidimenional data array. * @param string $format * @param int $statuscode * @param string $message * @param array $data * @param int|string $itemscount * @param int|string $itemsperpage * @since 8.1.0 * @deprecated 9.2.0 To implement an OCS endpoint extend the OCSController */ public function __construct($format, $statuscode, $message, $data=[], $itemscount='', $itemsperpage='') { $this->format = $format; $this->statuscode = $statuscode; $this->message = $message; $this->data = $data; $this->itemscount = $itemscount; $this->itemsperpage = $itemsperpage; // set the correct header based on the format parameter if ($format === 'json') { $this->addHeader( 'Content-Type', 'application/json; charset=utf-8' ); } else { $this->addHeader( 'Content-Type', 'application/xml; charset=utf-8' ); } } /** * @return string * @since 8.1.0 * @deprecated 9.2.0 To implement an OCS endpoint extend the OCSController */ public function render() { $r = new \OC_OCS_Result($this->data, $this->statuscode, $this->message); $r->setTotalItems($this->itemscount); $r->setItemsPerPage($this->itemsperpage); return \OC_API::renderResult($this->format, $r->getMeta(), $r->getData()); } } public/AppFramework/Http/EmptyContentSecurityPolicy.php 0000604 00000026661 15247130450 0017421 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; /** * Class EmptyContentSecurityPolicy is a simple helper which allows applications * to modify the Content-Security-Policy sent by ownCloud. Per default the policy * is forbidding everything. * * As alternative with sane exemptions look at ContentSecurityPolicy * * @see \OCP\AppFramework\Http\ContentSecurityPolicy * @package OCP\AppFramework\Http * @since 9.0.0 */ class EmptyContentSecurityPolicy { /** @var bool Whether inline JS snippets are allowed */ protected $inlineScriptAllowed = null; /** @var string Whether JS nonces should be used */ protected $useJsNonce = null; /** * @var bool Whether eval in JS scripts is allowed * TODO: Disallow per default * @link https://github.com/owncloud/core/issues/11925 */ protected $evalScriptAllowed = null; /** @var array Domains from which scripts can get loaded */ protected $allowedScriptDomains = null; /** * @var bool Whether inline CSS is allowed * TODO: Disallow per default * @link https://github.com/owncloud/core/issues/13458 */ protected $inlineStyleAllowed = null; /** @var array Domains from which CSS can get loaded */ protected $allowedStyleDomains = null; /** @var array Domains from which images can get loaded */ protected $allowedImageDomains = null; /** @var array Domains to which connections can be done */ protected $allowedConnectDomains = null; /** @var array Domains from which media elements can be loaded */ protected $allowedMediaDomains = null; /** @var array Domains from which object elements can be loaded */ protected $allowedObjectDomains = null; /** @var array Domains from which iframes can be loaded */ protected $allowedFrameDomains = null; /** @var array Domains from which fonts can be loaded */ protected $allowedFontDomains = null; /** @var array Domains from which web-workers and nested browsing content can load elements */ protected $allowedChildSrcDomains = null; /** * Whether inline JavaScript snippets are allowed or forbidden * @param bool $state * @return $this * @since 8.1.0 * @deprecated 10.0 CSP tokens are now used */ public function allowInlineScript($state = false) { $this->inlineScriptAllowed = $state; return $this; } /** * Use the according JS nonce * * @param string $nonce * @return $this * @since 11.0.0 */ public function useJsNonce($nonce) { $this->useJsNonce = $nonce; return $this; } /** * Whether eval in JavaScript is allowed or forbidden * @param bool $state * @return $this * @since 8.1.0 */ public function allowEvalScript($state = true) { $this->evalScriptAllowed = $state; return $this; } /** * Allows to execute JavaScript files from a specific domain. Use * to * allow JavaScript from all domains. * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedScriptDomain($domain) { $this->allowedScriptDomains[] = $domain; return $this; } /** * Remove the specified allowed script domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowScriptDomain($domain) { $this->allowedScriptDomains = array_diff($this->allowedScriptDomains, [$domain]); return $this; } /** * Whether inline CSS snippets are allowed or forbidden * @param bool $state * @return $this * @since 8.1.0 */ public function allowInlineStyle($state = true) { $this->inlineStyleAllowed = $state; return $this; } /** * Allows to execute CSS files from a specific domain. Use * to allow * CSS from all domains. * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedStyleDomain($domain) { $this->allowedStyleDomains[] = $domain; return $this; } /** * Remove the specified allowed style domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowStyleDomain($domain) { $this->allowedStyleDomains = array_diff($this->allowedStyleDomains, [$domain]); return $this; } /** * Allows using fonts from a specific domain. Use * to allow * fonts from all domains. * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedFontDomain($domain) { $this->allowedFontDomains[] = $domain; return $this; } /** * Remove the specified allowed font domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowFontDomain($domain) { $this->allowedFontDomains = array_diff($this->allowedFontDomains, [$domain]); return $this; } /** * Allows embedding images from a specific domain. Use * to allow * images from all domains. * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedImageDomain($domain) { $this->allowedImageDomains[] = $domain; return $this; } /** * Remove the specified allowed image domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowImageDomain($domain) { $this->allowedImageDomains = array_diff($this->allowedImageDomains, [$domain]); return $this; } /** * To which remote domains the JS connect to. * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedConnectDomain($domain) { $this->allowedConnectDomains[] = $domain; return $this; } /** * Remove the specified allowed connect domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowConnectDomain($domain) { $this->allowedConnectDomains = array_diff($this->allowedConnectDomains, [$domain]); return $this; } /** * From which domains media elements can be embedded. * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedMediaDomain($domain) { $this->allowedMediaDomains[] = $domain; return $this; } /** * Remove the specified allowed media domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowMediaDomain($domain) { $this->allowedMediaDomains = array_diff($this->allowedMediaDomains, [$domain]); return $this; } /** * From which domains objects such as <object>, <embed> or <applet> are executed * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedObjectDomain($domain) { $this->allowedObjectDomains[] = $domain; return $this; } /** * Remove the specified allowed object domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowObjectDomain($domain) { $this->allowedObjectDomains = array_diff($this->allowedObjectDomains, [$domain]); return $this; } /** * Which domains can be embedded in an iframe * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedFrameDomain($domain) { $this->allowedFrameDomains[] = $domain; return $this; } /** * Remove the specified allowed frame domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowFrameDomain($domain) { $this->allowedFrameDomains = array_diff($this->allowedFrameDomains, [$domain]); return $this; } /** * Domains from which web-workers and nested browsing content can load elements * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized. * @return $this * @since 8.1.0 */ public function addAllowedChildSrcDomain($domain) { $this->allowedChildSrcDomains[] = $domain; return $this; } /** * Remove the specified allowed child src domain from the allowed domains. * * @param string $domain * @return $this * @since 8.1.0 */ public function disallowChildSrcDomain($domain) { $this->allowedChildSrcDomains = array_diff($this->allowedChildSrcDomains, [$domain]); return $this; } /** * Get the generated Content-Security-Policy as a string * @return string * @since 8.1.0 */ public function buildPolicy() { $policy = "default-src 'none';"; $policy .= "base-uri 'none';"; $policy .= "manifest-src 'self';"; if(!empty($this->allowedScriptDomains) || $this->inlineScriptAllowed || $this->evalScriptAllowed) { $policy .= 'script-src '; if(is_string($this->useJsNonce)) { $policy .= '\'nonce-'.base64_encode($this->useJsNonce).'\''; $allowedScriptDomains = array_flip($this->allowedScriptDomains); unset($allowedScriptDomains['\'self\'']); $this->allowedScriptDomains = array_flip($allowedScriptDomains); if(count($allowedScriptDomains) !== 0) { $policy .= ' '; } } if(is_array($this->allowedScriptDomains)) { $policy .= implode(' ', $this->allowedScriptDomains); } if($this->inlineScriptAllowed) { $policy .= ' \'unsafe-inline\''; } if($this->evalScriptAllowed) { $policy .= ' \'unsafe-eval\''; } $policy .= ';'; } if(!empty($this->allowedStyleDomains) || $this->inlineStyleAllowed) { $policy .= 'style-src '; if(is_array($this->allowedStyleDomains)) { $policy .= implode(' ', $this->allowedStyleDomains); } if($this->inlineStyleAllowed) { $policy .= ' \'unsafe-inline\''; } $policy .= ';'; } if(!empty($this->allowedImageDomains)) { $policy .= 'img-src ' . implode(' ', $this->allowedImageDomains); $policy .= ';'; } if(!empty($this->allowedFontDomains)) { $policy .= 'font-src ' . implode(' ', $this->allowedFontDomains); $policy .= ';'; } if(!empty($this->allowedConnectDomains)) { $policy .= 'connect-src ' . implode(' ', $this->allowedConnectDomains); $policy .= ';'; } if(!empty($this->allowedMediaDomains)) { $policy .= 'media-src ' . implode(' ', $this->allowedMediaDomains); $policy .= ';'; } if(!empty($this->allowedObjectDomains)) { $policy .= 'object-src ' . implode(' ', $this->allowedObjectDomains); $policy .= ';'; } if(!empty($this->allowedFrameDomains)) { $policy .= 'frame-src ' . implode(' ', $this->allowedFrameDomains); $policy .= ';'; } if(!empty($this->allowedChildSrcDomains)) { $policy .= 'child-src ' . implode(' ', $this->allowedChildSrcDomains); $policy .= ';'; } return rtrim($policy, ';'); } } public/AppFramework/Http/TemplateResponse.php 0000604 00000007616 15247130450 0015351 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\HTTP\TemplateResponse class */ namespace OCP\AppFramework\Http; /** * Response for a normal template * @since 6.0.0 */ class TemplateResponse extends Response { /** * name of the template * @var string */ protected $templateName; /** * parameters * @var array */ protected $params; /** * rendering type (admin, user, blank) * @var string */ protected $renderAs; /** * app name * @var string */ protected $appName; /** * constructor of TemplateResponse * @param string $appName the name of the app to load the template from * @param string $templateName the name of the template * @param array $params an array of parameters which should be passed to the * template * @param string $renderAs how the page should be rendered, defaults to user * @since 6.0.0 - parameters $params and $renderAs were added in 7.0.0 */ public function __construct($appName, $templateName, array $params=array(), $renderAs='user') { $this->templateName = $templateName; $this->appName = $appName; $this->params = $params; $this->renderAs = $renderAs; } /** * Sets template parameters * @param array $params an array with key => value structure which sets template * variables * @return TemplateResponse Reference to this object * @since 6.0.0 - return value was added in 7.0.0 */ public function setParams(array $params){ $this->params = $params; return $this; } /** * Used for accessing the set parameters * @return array the params * @since 6.0.0 */ public function getParams(){ return $this->params; } /** * Used for accessing the name of the set template * @return string the name of the used template * @since 6.0.0 */ public function getTemplateName(){ return $this->templateName; } /** * Sets the template page * @param string $renderAs admin, user or blank. Admin also prints the admin * settings header and footer, user renders the normal * normal page including footer and header and blank * just renders the plain template * @return TemplateResponse Reference to this object * @since 6.0.0 - return value was added in 7.0.0 */ public function renderAs($renderAs){ $this->renderAs = $renderAs; return $this; } /** * Returns the set renderAs * @return string the renderAs value * @since 6.0.0 */ public function getRenderAs(){ return $this->renderAs; } /** * Returns the rendered html * @return string the rendered html * @since 6.0.0 */ public function render(){ // \OCP\Template needs an empty string instead of 'blank' for an unwrapped response $renderAs = $this->renderAs === 'blank' ? '' : $this->renderAs; $template = new \OCP\Template($this->appName, $this->templateName, $renderAs); foreach($this->params as $key => $value){ $template->assign($key, $value); } return $template->fetchPage(); } } public/AppFramework/Http/DownloadResponse.php 0000604 00000003065 15247130450 0015337 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; /** * Prompts the user to download the a file * @since 7.0.0 */ class DownloadResponse extends \OCP\AppFramework\Http\Response { private $filename; private $contentType; /** * Creates a response that prompts the user to download the file * @param string $filename the name that the downloaded file should have * @param string $contentType the mimetype that the downloaded file should have * @since 7.0.0 */ public function __construct($filename, $contentType) { $this->filename = $filename; $this->contentType = $contentType; $this->addHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); $this->addHeader('Content-Type', $contentType); } } public/AppFramework/Http/DataDownloadResponse.php 0000604 00000003177 15247130450 0016135 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; /** * Class DataDownloadResponse * * @package OCP\AppFramework\Http * @since 8.0.0 */ class DataDownloadResponse extends DownloadResponse { /** * @var string */ private $data; /** * Creates a response that prompts the user to download the text * @param string $data text to be downloaded * @param string $filename the name that the downloaded file should have * @param string $contentType the mimetype that the downloaded file should have * @since 8.0.0 */ public function __construct($data, $filename, $contentType) { $this->data = $data; parent::__construct($filename, $contentType); } /** * @param string $data * @since 8.0.0 */ public function setData($data) { $this->data = $data; } /** * @return string * @since 8.0.0 */ public function render() { return $this->data; } } public/AppFramework/Http/DataResponse.php 0000604 00000004075 15247130450 0014443 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\HTTP\DataResponse class */ namespace OCP\AppFramework\Http; use OCP\AppFramework\Http; /** * A generic DataResponse class that is used to return generic data responses * for responders to transform * @since 8.0.0 */ class DataResponse extends Response { /** * response data * @var array|object */ protected $data; /** * @param array|object $data the object or array that should be transformed * @param int $statusCode the Http status code, defaults to 200 * @param array $headers additional key value based headers * @since 8.0.0 */ public function __construct($data=array(), $statusCode=Http::STATUS_OK, array $headers=array()) { $this->data = $data; $this->setStatus($statusCode); $this->setHeaders(array_merge($this->getHeaders(), $headers)); } /** * Sets values in the data json array * @param array|object $data an array or object which will be transformed * @return DataResponse Reference to this object * @since 8.0.0 */ public function setData($data){ $this->data = $data; return $this; } /** * Used to get the set parameters * @return array the data * @since 8.0.0 */ public function getData(){ return $this->data; } } public/AppFramework/Http/DataDisplayResponse.php 0000604 00000004052 15247130450 0015764 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; use OCP\AppFramework\Http; /** * Class DataDisplayResponse * * @package OCP\AppFramework\Http * @since 8.1.0 */ class DataDisplayResponse extends Response { /** * response data * @var string; */ protected $data; /** * @param string $data the data to display * @param int $statusCode the Http status code, defaults to 200 * @param array $headers additional key value based headers * @since 8.1.0 */ public function __construct($data="", $statusCode=Http::STATUS_OK, $headers=[]) { $this->data = $data; $this->setStatus($statusCode); $this->setHeaders(array_merge($this->getHeaders(), $headers)); $this->addHeader('Content-Disposition', 'inline; filename=""'); } /** * Outputs data. No processing is done. * @return string * @since 8.1.0 */ public function render() { return $this->data; } /** * Sets values in the data * @param string $data the data to display * @return DataDisplayResponse Reference to this object * @since 8.1.0 */ public function setData($data){ $this->data = $data; return $this; } /** * Used to get the set parameters * @return string the data * @since 8.1.0 */ public function getData(){ return $this->data; } } public/AppFramework/Http/ContentSecurityPolicy.php 0000604 00000005672 15247130450 0016401 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author sualko <klaus@jsxc.org> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; /** * Class ContentSecurityPolicy is a simple helper which allows applications to * modify the Content-Security-Policy sent by ownCloud. Per default only JavaScript, * stylesheets, images, fonts, media and connections from the same domain * ('self') are allowed. * * Even if a value gets modified above defaults will still get appended. Please * notice that ownCloud ships already with sensible defaults and those policies * should require no modification at all for most use-cases. * * @package OCP\AppFramework\Http * @since 8.1.0 */ class ContentSecurityPolicy extends EmptyContentSecurityPolicy { /** @var bool Whether inline JS snippets are allowed */ protected $inlineScriptAllowed = false; /** * @var bool Whether eval in JS scripts is allowed * TODO: Disallow per default * @link https://github.com/owncloud/core/issues/11925 */ protected $evalScriptAllowed = true; /** @var array Domains from which scripts can get loaded */ protected $allowedScriptDomains = [ '\'self\'', ]; /** * @var bool Whether inline CSS is allowed * TODO: Disallow per default * @link https://github.com/owncloud/core/issues/13458 */ protected $inlineStyleAllowed = true; /** @var array Domains from which CSS can get loaded */ protected $allowedStyleDomains = [ '\'self\'', ]; /** @var array Domains from which images can get loaded */ protected $allowedImageDomains = [ '\'self\'', 'data:', 'blob:', ]; /** @var array Domains to which connections can be done */ protected $allowedConnectDomains = [ '\'self\'', ]; /** @var array Domains from which media elements can be loaded */ protected $allowedMediaDomains = [ '\'self\'', ]; /** @var array Domains from which object elements can be loaded */ protected $allowedObjectDomains = []; /** @var array Domains from which iframes can be loaded */ protected $allowedFrameDomains = []; /** @var array Domains from which fonts can be loaded */ protected $allowedFontDomains = [ '\'self\'', ]; /** @var array Domains from which web-workers and nested browsing content can load elements */ protected $allowedChildSrcDomains = []; } public/AppFramework/Http/Response.php 0000604 00000017136 15247130450 0013653 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\HTTP\Response class */ namespace OCP\AppFramework\Http; use OCP\AppFramework\Http; /** * Base class for responses. Also used to just send headers. * * It handles headers, HTTP status code, last modified and ETag. * @since 6.0.0 */ class Response { /** * Headers - defaults to ['Cache-Control' => 'no-cache, no-store, must-revalidate'] * @var array */ private $headers = array( 'Cache-Control' => 'no-cache, no-store, must-revalidate' ); /** * Cookies that will be need to be constructed as header * @var array */ private $cookies = array(); /** * HTTP status code - defaults to STATUS OK * @var int */ private $status = Http::STATUS_OK; /** * Last modified date * @var \DateTime */ private $lastModified; /** * ETag * @var string */ private $ETag; /** @var ContentSecurityPolicy|null Used Content-Security-Policy */ private $contentSecurityPolicy = null; /** @var bool */ private $throttled = false; /** * Caches the response * @param int $cacheSeconds the amount of seconds that should be cached * if 0 then caching will be disabled * @return $this * @since 6.0.0 - return value was added in 7.0.0 */ public function cacheFor($cacheSeconds) { if($cacheSeconds > 0) { $this->addHeader('Cache-Control', 'max-age=' . $cacheSeconds . ', must-revalidate'); } else { $this->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); } return $this; } /** * Adds a new cookie to the response * @param string $name The name of the cookie * @param string $value The value of the cookie * @param \DateTime|null $expireDate Date on that the cookie should expire, if set * to null cookie will be considered as session * cookie. * @return $this * @since 8.0.0 */ public function addCookie($name, $value, \DateTime $expireDate = null) { $this->cookies[$name] = array('value' => $value, 'expireDate' => $expireDate); return $this; } /** * Set the specified cookies * @param array $cookies array('foo' => array('value' => 'bar', 'expire' => null)) * @return $this * @since 8.0.0 */ public function setCookies(array $cookies) { $this->cookies = $cookies; return $this; } /** * Invalidates the specified cookie * @param string $name * @return $this * @since 8.0.0 */ public function invalidateCookie($name) { $this->addCookie($name, 'expired', new \DateTime('1971-01-01 00:00')); return $this; } /** * Invalidates the specified cookies * @param array $cookieNames array('foo', 'bar') * @return $this * @since 8.0.0 */ public function invalidateCookies(array $cookieNames) { foreach($cookieNames as $cookieName) { $this->invalidateCookie($cookieName); } return $this; } /** * Returns the cookies * @return array * @since 8.0.0 */ public function getCookies() { return $this->cookies; } /** * Adds a new header to the response that will be called before the render * function * @param string $name The name of the HTTP header * @param string $value The value, null will delete it * @return $this * @since 6.0.0 - return value was added in 7.0.0 */ public function addHeader($name, $value) { $name = trim($name); // always remove leading and trailing whitespace // to be able to reliably check for security // headers if(is_null($value)) { unset($this->headers[$name]); } else { $this->headers[$name] = $value; } return $this; } /** * Set the headers * @param array $headers value header pairs * @return $this * @since 8.0.0 */ public function setHeaders(array $headers) { $this->headers = $headers; return $this; } /** * Returns the set headers * @return array the headers * @since 6.0.0 */ public function getHeaders() { $mergeWith = []; if($this->lastModified) { $mergeWith['Last-Modified'] = $this->lastModified->format(\DateTime::RFC2822); } // Build Content-Security-Policy and use default if none has been specified if(is_null($this->contentSecurityPolicy)) { $this->setContentSecurityPolicy(new ContentSecurityPolicy()); } $this->headers['Content-Security-Policy'] = $this->contentSecurityPolicy->buildPolicy(); if($this->ETag) { $mergeWith['ETag'] = '"' . $this->ETag . '"'; } return array_merge($mergeWith, $this->headers); } /** * By default renders no output * @return null * @since 6.0.0 */ public function render() { return null; } /** * Set response status * @param int $status a HTTP status code, see also the STATUS constants * @return Response Reference to this object * @since 6.0.0 - return value was added in 7.0.0 */ public function setStatus($status) { $this->status = $status; return $this; } /** * Set a Content-Security-Policy * @param EmptyContentSecurityPolicy $csp Policy to set for the response object * @return $this * @since 8.1.0 */ public function setContentSecurityPolicy(EmptyContentSecurityPolicy $csp) { $this->contentSecurityPolicy = $csp; return $this; } /** * Get the currently used Content-Security-Policy * @return EmptyContentSecurityPolicy|null Used Content-Security-Policy or null if * none specified. * @since 8.1.0 */ public function getContentSecurityPolicy() { return $this->contentSecurityPolicy; } /** * Get response status * @since 6.0.0 */ public function getStatus() { return $this->status; } /** * Get the ETag * @return string the etag * @since 6.0.0 */ public function getETag() { return $this->ETag; } /** * Get "last modified" date * @return \DateTime RFC2822 formatted last modified date * @since 6.0.0 */ public function getLastModified() { return $this->lastModified; } /** * Set the ETag * @param string $ETag * @return Response Reference to this object * @since 6.0.0 - return value was added in 7.0.0 */ public function setETag($ETag) { $this->ETag = $ETag; return $this; } /** * Set "last modified" date * @param \DateTime $lastModified * @return Response Reference to this object * @since 6.0.0 - return value was added in 7.0.0 */ public function setLastModified($lastModified) { $this->lastModified = $lastModified; return $this; } /** * Marks the response as to throttle. Will be throttled when the * @BruteForceProtection annotation is added. * * @since 12.0.0 */ public function throttle() { $this->throttled = true; } /** * Whether the current response is throttled. * * @since 12.0.0 */ public function isThrottled() { return $this->throttled; } } public/AppFramework/Http/ICallbackResponse.php 0000604 00000002252 15247130450 0015372 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; /** * Interface ICallbackResponse * * @package OCP\AppFramework\Http * @since 8.1.0 */ interface ICallbackResponse { /** * Outputs the content that should be printed * * @param IOutput $output a small wrapper that handles output * @since 8.1.0 */ function callback(IOutput $output); } public/AppFramework/Http/NotFoundResponse.php 0000604 00000002341 15247130450 0015320 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; use OCP\Template; /** * A generic 404 response showing an 404 error page as well to the end-user * @since 8.1.0 */ class NotFoundResponse extends Response { /** * @since 8.1.0 */ public function __construct() { $this->setStatus(404); } /** * @return string * @since 8.1.0 */ public function render() { $template = new Template('core', '404', 'guest'); return $template->fetchPage(); } } public/AppFramework/Http/RedirectResponse.php 0000604 00000003001 15247130450 0015317 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author v1r0x <vinzenz.rosenkranz@gmail.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; use OCP\AppFramework\Http; /** * Redirects to a different URL * @since 7.0.0 */ class RedirectResponse extends Response { private $redirectURL; /** * Creates a response that redirects to a url * @param string $redirectURL the url to redirect to * @since 7.0.0 */ public function __construct($redirectURL) { $this->redirectURL = $redirectURL; $this->setStatus(Http::STATUS_SEE_OTHER); $this->addHeader('Location', $redirectURL); } /** * @return string the url to redirect * @since 7.0.0 */ public function getRedirectURL() { return $this->redirectURL; } } public/AppFramework/Http/IOutput.php 0000604 00000003550 15247130450 0013461 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; /** * Very thin wrapper class to make output testable * @since 8.1.0 */ interface IOutput { /** * @param string $out * @since 8.1.0 */ public function setOutput($out); /** * @param string|resource $path or file handle * * @return bool false if an error occurred * @since 8.1.0 */ public function setReadfile($path); /** * @param string $header * @since 8.1.0 */ public function setHeader($header); /** * @return int returns the current http response code * @since 8.1.0 */ public function getHttpResponseCode(); /** * @param int $code sets the http status code * @since 8.1.0 */ public function setHttpResponseCode($code); /** * @param string $name * @param string $value * @param int $expire * @param string $path * @param string $domain * @param bool $secure * @param bool $httpOnly * @since 8.1.0 */ public function setCookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); } public/AppFramework/Http/StreamResponse.php 0000604 00000003477 15247130450 0015032 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Http; use OCP\AppFramework\Http; /** * Class StreamResponse * * @package OCP\AppFramework\Http * @since 8.1.0 */ class StreamResponse extends Response implements ICallbackResponse { /** @var string */ private $filePath; /** * @param string|resource $filePath the path to the file or a file handle which should be streamed * @since 8.1.0 */ public function __construct ($filePath) { $this->filePath = $filePath; } /** * Streams the file using readfile * * @param IOutput $output a small wrapper that handles output * @since 8.1.0 */ public function callback (IOutput $output) { // handle caching if ($output->getHttpResponseCode() !== Http::STATUS_NOT_MODIFIED) { if (!(is_resource($this->filePath) || file_exists($this->filePath))) { $output->setHttpResponseCode(Http::STATUS_NOT_FOUND); } elseif ($output->setReadfile($this->filePath) === false) { $output->setHttpResponseCode(Http::STATUS_BAD_REQUEST); } } } } public/AppFramework/Http/FileDisplayResponse.php 0000604 00000004117 15247130450 0015774 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\AppFramework\Http; use OCP\AppFramework\Http; /** * Class FileDisplayResponse * * @package OCP\AppFramework\Http * @since 11.0.0 */ class FileDisplayResponse extends Response implements ICallbackResponse { /** @var \OCP\Files\File|\OCP\Files\SimpleFS\ISimpleFile */ private $file; /** * FileDisplayResponse constructor. * * @param \OCP\Files\File|\OCP\Files\SimpleFS\ISimpleFile $file * @param int $statusCode * @param array $headers * @since 11.0.0 */ public function __construct($file, $statusCode=Http::STATUS_OK, $headers=[]) { $this->file = $file; $this->setStatus($statusCode); $this->setHeaders(array_merge($this->getHeaders(), $headers)); $this->addHeader('Content-Disposition', 'inline; filename="' . rawurldecode($file->getName()) . '"'); $this->setETag($file->getEtag()); $lastModified = new \DateTime(); $lastModified->setTimestamp($file->getMTime()); $this->setLastModified($lastModified); } /** * @param IOutput $output * @since 11.0.0 */ public function callback(IOutput $output) { if ($output->getHttpResponseCode() !== Http::STATUS_NOT_MODIFIED) { $output->setHeader('Content-Length: ' . $this->file->getSize()); $output->setOutput($this->file->getContent()); } } } public/AppFramework/Http/JSONResponse.php 0000604 00000005061 15247130450 0014337 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\HTTP\JSONResponse class */ namespace OCP\AppFramework\Http; use OCP\AppFramework\Http; /** * A renderer for JSON calls * @since 6.0.0 */ class JSONResponse extends Response { /** * response data * @var array|object */ protected $data; /** * constructor of JSONResponse * @param array|object $data the object or array that should be transformed * @param int $statusCode the Http status code, defaults to 200 * @since 6.0.0 */ public function __construct($data=array(), $statusCode=Http::STATUS_OK) { $this->data = $data; $this->setStatus($statusCode); $this->addHeader('Content-Type', 'application/json; charset=utf-8'); } /** * Returns the rendered json * @return string the rendered json * @since 6.0.0 * @throws \Exception If data could not get encoded */ public function render() { $response = json_encode($this->data, JSON_HEX_TAG); if($response === false) { throw new \Exception(sprintf('Could not json_encode due to invalid ' . 'non UTF-8 characters in the array: %s', var_export($this->data, true))); } return $response; } /** * Sets values in the data json array * @param array|object $data an array or object which will be transformed * to JSON * @return JSONResponse Reference to this object * @since 6.0.0 - return value was added in 7.0.0 */ public function setData($data){ $this->data = $data; return $this; } /** * Used to get the set parameters * @return array the data * @since 6.0.0 */ public function getData(){ return $this->data; } } public/AppFramework/App.php 0000604 00000010073 15247130450 0011647 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework/App class */ namespace OCP\AppFramework; use OC\AppFramework\Routing\RouteConfig; /** * Class App * @package OCP\AppFramework * * Any application must inherit this call - all controller instances to be used are * to be registered using IContainer::registerService * @since 6.0.0 */ class App { /** * Turns an app id into a namespace by convetion. The id is split at the * underscores, all parts are camelcased and reassembled. e.g.: * some_app_id -> OCA\SomeAppId * @param string $appId the app id * @param string $topNamespace the namespace which should be prepended to * the transformed app id, defaults to OCA\ * @return string the starting namespace for the app * @since 8.0.0 */ public static function buildAppNamespace($appId, $topNamespace='OCA\\') { return \OC\AppFramework\App::buildAppNamespace($appId, $topNamespace); } /** * @param array $urlParams an array with variables extracted from the routes * @since 6.0.0 */ public function __construct($appName, $urlParams = array()) { $this->container = new \OC\AppFramework\DependencyInjection\DIContainer($appName, $urlParams); } private $container; /** * @return IAppContainer * @since 6.0.0 */ public function getContainer() { return $this->container; } /** * This function is to be called to create single routes and restful routes based on the given $routes array. * * Example code in routes.php of tasks app (it will register two restful resources): * $routes = array( * 'resources' => array( * 'lists' => array('url' => '/tasklists'), * 'tasks' => array('url' => '/tasklists/{listId}/tasks') * ) * ); * * $a = new TasksApp(); * $a->registerRoutes($this, $routes); * * @param \OCP\Route\IRouter $router * @param array $routes * @since 6.0.0 */ public function registerRoutes($router, $routes) { $routeConfig = new RouteConfig($this->container, $router, $routes); $routeConfig->register(); } /** * This function is called by the routing component to fire up the frameworks dispatch mechanism. * * Example code in routes.php of the task app: * $this->create('tasks_index', '/')->get()->action( * function($params){ * $app = new TaskApp($params); * $app->dispatch('PageController', 'index'); * } * ); * * * Example for for TaskApp implementation: * class TaskApp extends \OCP\AppFramework\App { * * public function __construct($params){ * parent::__construct('tasks', $params); * * $this->getContainer()->registerService('PageController', function(IAppContainer $c){ * $a = $c->query('API'); * $r = $c->query('Request'); * return new PageController($a, $r); * }); * } * } * * @param string $controllerName the name of the controller under which it is * stored in the DI container * @param string $methodName the method that you want to call * @since 6.0.0 */ public function dispatch($controllerName, $methodName) { \OC\AppFramework\App::main($controllerName, $methodName, $this->container); } } public/AppFramework/Utility/IControllerMethodReflector.php 0000604 00000003637 15247130450 0020045 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Utility; /** * Interface ControllerMethodReflector * * Reads and parses annotations from doc comments * * @package OCP\AppFramework\Utility * @since 8.0.0 */ interface IControllerMethodReflector { /** * @param object $object an object or classname * @param string $method the method which we want to inspect * @return void * @since 8.0.0 */ public function reflect($object, $method); /** * Inspects the PHPDoc parameters for types * * @param string $parameter the parameter whose type comments should be * parsed * @return string|null type in the type parameters (@param int $something) * would return int or null if not existing * @since 8.0.0 */ public function getType($parameter); /** * @return array the arguments of the method with key => default value * @since 8.0.0 */ public function getParameters(); /** * Check if a method contains an annotation * * @param string $name the name of the annotation * @return bool true if the annotation is found * @since 8.0.0 */ public function hasAnnotation($name); } public/AppFramework/Utility/ITimeFactory.php 0000604 00000002014 15247130450 0015125 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Utility; /** * Needed to mock calls to time() * @since 8.0.0 */ interface ITimeFactory { /** * @return int the result of a call to time() * @since 8.0.0 */ public function getTime(); } public/AppFramework/IAppContainer.php 0000604 00000004431 15247130450 0013624 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework; use OCP\IContainer; /** * Class IAppContainer * @package OCP\AppFramework * * This container interface provides short cuts for app developers to access predefined app service. * @since 6.0.0 */ interface IAppContainer extends IContainer { /** * used to return the appname of the set application * @return string the name of your application * @since 6.0.0 */ function getAppName(); /** * @deprecated 8.0.0 implements only deprecated methods * @return IApi * @since 6.0.0 */ function getCoreApi(); /** * @return \OCP\IServerContainer * @since 6.0.0 */ function getServer(); /** * @param string $middleWare * @return boolean * @since 6.0.0 */ function registerMiddleWare($middleWare); /** * @deprecated 8.0.0 use IUserSession->isLoggedIn() * @return boolean * @since 6.0.0 */ function isLoggedIn(); /** * @deprecated 8.0.0 use IGroupManager->isAdmin($userId) * @return boolean * @since 6.0.0 */ function isAdminUser(); /** * @deprecated 8.0.0 use the ILogger instead * @param string $message * @param string $level * @return mixed * @since 6.0.0 */ function log($message, $level); /** * Register a capability * * @param string $serviceName e.g. 'OCA\Files\Capabilities' * @since 8.2.0 */ public function registerCapability($serviceName); } public/AppFramework/ApiController.php 0000604 00000006524 15247130450 0013712 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\Controller class */ namespace OCP\AppFramework; use OCP\AppFramework\Http\Response; use OCP\IRequest; /** * Base class to inherit your controllers from that are used for RESTful APIs * @since 7.0.0 */ abstract class ApiController extends Controller { private $corsMethods; private $corsAllowedHeaders; private $corsMaxAge; /** * constructor of the controller * @param string $appName the name of the app * @param IRequest $request an instance of the request * @param string $corsMethods comma separated string of HTTP verbs which * should be allowed for websites or webapps when calling your API, defaults to * 'PUT, POST, GET, DELETE, PATCH' * @param string $corsAllowedHeaders comma separated string of HTTP headers * which should be allowed for websites or webapps when calling your API, * defaults to 'Authorization, Content-Type, Accept' * @param int $corsMaxAge number in seconds how long a preflighted OPTIONS * request should be cached, defaults to 1728000 seconds * @since 7.0.0 */ public function __construct($appName, IRequest $request, $corsMethods='PUT, POST, GET, DELETE, PATCH', $corsAllowedHeaders='Authorization, Content-Type, Accept', $corsMaxAge=1728000){ parent::__construct($appName, $request); $this->corsMethods = $corsMethods; $this->corsAllowedHeaders = $corsAllowedHeaders; $this->corsMaxAge = $corsMaxAge; } /** * This method implements a preflighted cors response for you that you can * link to for the options request * * @NoAdminRequired * @NoCSRFRequired * @PublicPage * @since 7.0.0 */ public function preflightedCors() { if(isset($this->request->server['HTTP_ORIGIN'])) { $origin = $this->request->server['HTTP_ORIGIN']; } else { $origin = '*'; } $response = new Response(); $response->addHeader('Access-Control-Allow-Origin', $origin); $response->addHeader('Access-Control-Allow-Methods', $this->corsMethods); $response->addHeader('Access-Control-Max-Age', $this->corsMaxAge); $response->addHeader('Access-Control-Allow-Headers', $this->corsAllowedHeaders); $response->addHeader('Access-Control-Allow-Credentials', 'false'); return $response; } } public/AppFramework/Db/MultipleObjectsReturnedException.php 0000604 00000002223 15247130450 0020147 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Db; /** * This is returned or should be returned when a find request finds more than one * row * @since 7.0.0 */ class MultipleObjectsReturnedException extends \Exception { /** * Constructor * @param string $msg the error message * @since 7.0.0 */ public function __construct($msg){ parent::__construct($msg); } } public/AppFramework/Db/Mapper.php 0000604 00000023543 15247130450 0012706 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Db; use OCP\IDBConnection; /** * Simple parent class for inheriting your data access layer from. This class * may be subject to change in the future * @since 7.0.0 */ abstract class Mapper { protected $tableName; protected $entityClass; protected $db; /** * @param IDBConnection $db Instance of the Db abstraction layer * @param string $tableName the name of the table. set this to allow entity * @param string $entityClass the name of the entity that the sql should be * mapped to queries without using sql * @since 7.0.0 */ public function __construct(IDBConnection $db, $tableName, $entityClass=null){ $this->db = $db; $this->tableName = '*PREFIX*' . $tableName; // if not given set the entity name to the class without the mapper part // cache it here for later use since reflection is slow if($entityClass === null) { $this->entityClass = str_replace('Mapper', '', get_class($this)); } else { $this->entityClass = $entityClass; } } /** * @return string the table name * @since 7.0.0 */ public function getTableName(){ return $this->tableName; } /** * Deletes an entity from the table * @param Entity $entity the entity that should be deleted * @return Entity the deleted entity * @since 7.0.0 - return value added in 8.1.0 */ public function delete(Entity $entity){ $sql = 'DELETE FROM `' . $this->tableName . '` WHERE `id` = ?'; $stmt = $this->execute($sql, [$entity->getId()]); $stmt->closeCursor(); return $entity; } /** * Creates a new entry in the db from an entity * @param Entity $entity the entity that should be created * @return Entity the saved entity with the set id * @since 7.0.0 */ public function insert(Entity $entity){ // get updated fields to save, fields have to be set using a setter to // be saved $properties = $entity->getUpdatedFields(); $values = ''; $columns = ''; $params = []; // build the fields $i = 0; foreach($properties as $property => $updated) { $column = $entity->propertyToColumn($property); $getter = 'get' . ucfirst($property); $columns .= '`' . $column . '`'; $values .= '?'; // only append colon if there are more entries if($i < count($properties)-1){ $columns .= ','; $values .= ','; } $params[] = $entity->$getter(); $i++; } $sql = 'INSERT INTO `' . $this->tableName . '`(' . $columns . ') VALUES(' . $values . ')'; $stmt = $this->execute($sql, $params); $entity->setId((int) $this->db->lastInsertId($this->tableName)); $stmt->closeCursor(); return $entity; } /** * Updates an entry in the db from an entity * @throws \InvalidArgumentException if entity has no id * @param Entity $entity the entity that should be created * @return Entity the saved entity with the set id * @since 7.0.0 - return value was added in 8.0.0 */ public function update(Entity $entity){ // if entity wasn't changed it makes no sense to run a db query $properties = $entity->getUpdatedFields(); if(count($properties) === 0) { return $entity; } // entity needs an id $id = $entity->getId(); if($id === null){ throw new \InvalidArgumentException( 'Entity which should be updated has no id'); } // get updated fields to save, fields have to be set using a setter to // be saved // do not update the id field unset($properties['id']); $columns = ''; $params = []; // build the fields $i = 0; foreach($properties as $property => $updated) { $column = $entity->propertyToColumn($property); $getter = 'get' . ucfirst($property); $columns .= '`' . $column . '` = ?'; // only append colon if there are more entries if($i < count($properties)-1){ $columns .= ','; } $params[] = $entity->$getter(); $i++; } $sql = 'UPDATE `' . $this->tableName . '` SET ' . $columns . ' WHERE `id` = ?'; $params[] = $id; $stmt = $this->execute($sql, $params); $stmt->closeCursor(); return $entity; } /** * Checks if an array is associative * @param array $array * @return bool true if associative * @since 8.1.0 */ private function isAssocArray(array $array) { return array_values($array) !== $array; } /** * Returns the correct PDO constant based on the value type * @param $value * @return int PDO constant * @since 8.1.0 */ private function getPDOType($value) { switch (gettype($value)) { case 'integer': return \PDO::PARAM_INT; case 'boolean': return \PDO::PARAM_BOOL; default: return \PDO::PARAM_STR; } } /** * Runs an sql query * @param string $sql the prepare string * @param array $params the params which should replace the ? in the sql query * @param int $limit the maximum number of rows * @param int $offset from which row we want to start * @return \PDOStatement the database query result * @since 7.0.0 */ protected function execute($sql, array $params=[], $limit=null, $offset=null){ $query = $this->db->prepare($sql, $limit, $offset); if ($this->isAssocArray($params)) { foreach ($params as $key => $param) { $pdoConstant = $this->getPDOType($param); $query->bindValue($key, $param, $pdoConstant); } } else { $index = 1; // bindParam is 1 indexed foreach ($params as $param) { $pdoConstant = $this->getPDOType($param); $query->bindValue($index, $param, $pdoConstant); $index++; } } $result = $query->execute(); return $query; } /** * Returns an db result and throws exceptions when there are more or less * results * @see findEntity * @param string $sql the sql query * @param array $params the parameters of the sql query * @param int $limit the maximum number of rows * @param int $offset from which row we want to start * @throws DoesNotExistException if the item does not exist * @throws MultipleObjectsReturnedException if more than one item exist * @return array the result as row * @since 7.0.0 */ protected function findOneQuery($sql, array $params=[], $limit=null, $offset=null){ $stmt = $this->execute($sql, $params, $limit, $offset); $row = $stmt->fetch(); if($row === false || $row === null){ $stmt->closeCursor(); $msg = $this->buildDebugMessage( 'Did expect one result but found none when executing', $sql, $params, $limit, $offset ); throw new DoesNotExistException($msg); } $row2 = $stmt->fetch(); $stmt->closeCursor(); //MDB2 returns null, PDO and doctrine false when no row is available if( ! ($row2 === false || $row2 === null )) { $msg = $this->buildDebugMessage( 'Did not expect more than one result when executing', $sql, $params, $limit, $offset ); throw new MultipleObjectsReturnedException($msg); } else { return $row; } } /** * Builds an error message by prepending the $msg to an error message which * has the parameters * @see findEntity * @param string $sql the sql query * @param array $params the parameters of the sql query * @param int $limit the maximum number of rows * @param int $offset from which row we want to start * @return string formatted error message string * @since 9.1.0 */ private function buildDebugMessage($msg, $sql, array $params=[], $limit=null, $offset=null) { return $msg . ': query "' . $sql . '"; ' . 'parameters ' . print_r($params, true) . '; ' . 'limit "' . $limit . '"; '. 'offset "' . $offset . '"'; } /** * Creates an entity from a row. Automatically determines the entity class * from the current mapper name (MyEntityMapper -> MyEntity) * @param array $row the row which should be converted to an entity * @return Entity the entity * @since 7.0.0 */ protected function mapRowToEntity($row) { return call_user_func($this->entityClass .'::fromRow', $row); } /** * Runs a sql query and returns an array of entities * @param string $sql the prepare string * @param array $params the params which should replace the ? in the sql query * @param int $limit the maximum number of rows * @param int $offset from which row we want to start * @return array all fetched entities * @since 7.0.0 */ protected function findEntities($sql, array $params=[], $limit=null, $offset=null) { $stmt = $this->execute($sql, $params, $limit, $offset); $entities = []; while($row = $stmt->fetch()){ $entities[] = $this->mapRowToEntity($row); } $stmt->closeCursor(); return $entities; } /** * Returns an db result and throws exceptions when there are more or less * results * @param string $sql the sql query * @param array $params the parameters of the sql query * @param int $limit the maximum number of rows * @param int $offset from which row we want to start * @throws DoesNotExistException if the item does not exist * @throws MultipleObjectsReturnedException if more than one item exist * @return Entity the entity * @since 7.0.0 */ protected function findEntity($sql, array $params=[], $limit=null, $offset=null){ return $this->mapRowToEntity($this->findOneQuery($sql, $params, $limit, $offset)); } } public/AppFramework/Db/Entity.php 0000604 00000013657 15247130450 0012743 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Db; /** * @method integer getId() * @method void setId(integer $id) * @since 7.0.0 */ abstract class Entity { public $id; private $_updatedFields = array(); private $_fieldTypes = array('id' => 'integer'); /** * Simple alternative constructor for building entities from a request * @param array $params the array which was obtained via $this->params('key') * in the controller * @return Entity * @since 7.0.0 */ public static function fromParams(array $params) { $instance = new static(); foreach($params as $key => $value) { $method = 'set' . ucfirst($key); $instance->$method($value); } return $instance; } /** * Maps the keys of the row array to the attributes * @param array $row the row to map onto the entity * @since 7.0.0 */ public static function fromRow(array $row){ $instance = new static(); foreach($row as $key => $value){ $prop = ucfirst($instance->columnToProperty($key)); $setter = 'set' . $prop; $instance->$setter($value); } $instance->resetUpdatedFields(); return $instance; } /** * @return array with attribute and type * @since 7.0.0 */ public function getFieldTypes() { return $this->_fieldTypes; } /** * Marks the entity as clean needed for setting the id after the insertion * @since 7.0.0 */ public function resetUpdatedFields(){ $this->_updatedFields = array(); } /** * Generic setter for properties * @since 7.0.0 */ protected function setter($name, $args) { // setters should only work for existing attributes if(property_exists($this, $name)){ if($this->$name === $args[0]) { return; } $this->markFieldUpdated($name); // if type definition exists, cast to correct type if($args[0] !== null && array_key_exists($name, $this->_fieldTypes)) { settype($args[0], $this->_fieldTypes[$name]); } $this->$name = $args[0]; } else { throw new \BadFunctionCallException($name . ' is not a valid attribute'); } } /** * Generic getter for properties * @since 7.0.0 */ protected function getter($name) { // getters should only work for existing attributes if(property_exists($this, $name)){ return $this->$name; } else { throw new \BadFunctionCallException($name . ' is not a valid attribute'); } } /** * Each time a setter is called, push the part after set * into an array: for instance setId will save Id in the * updated fields array so it can be easily used to create the * getter method * @since 7.0.0 */ public function __call($methodName, $args){ $attr = lcfirst( substr($methodName, 3) ); if(strpos($methodName, 'set') === 0){ $this->setter($attr, $args); } elseif(strpos($methodName, 'get') === 0) { return $this->getter($attr); } else { throw new \BadFunctionCallException($methodName . ' does not exist'); } } /** * Mark am attribute as updated * @param string $attribute the name of the attribute * @since 7.0.0 */ protected function markFieldUpdated($attribute){ $this->_updatedFields[$attribute] = true; } /** * Transform a database columnname to a property * @param string $columnName the name of the column * @return string the property name * @since 7.0.0 */ public function columnToProperty($columnName){ $parts = explode('_', $columnName); $property = null; foreach($parts as $part){ if($property === null){ $property = $part; } else { $property .= ucfirst($part); } } return $property; } /** * Transform a property to a database column name * @param string $property the name of the property * @return string the column name * @since 7.0.0 */ public function propertyToColumn($property){ $parts = preg_split('/(?=[A-Z])/', $property); $column = null; foreach($parts as $part){ if($column === null){ $column = $part; } else { $column .= '_' . lcfirst($part); } } return $column; } /** * @return array array of updated fields for update query * @since 7.0.0 */ public function getUpdatedFields(){ return $this->_updatedFields; } /** * Adds type information for a field so that its automatically casted to * that value once its being returned from the database * @param string $fieldName the name of the attribute * @param string $type the type which will be used to call settype() * @since 7.0.0 */ protected function addType($fieldName, $type){ $this->_fieldTypes[$fieldName] = $type; } /** * Slugify the value of a given attribute * Warning: This doesn't result in a unique value * @param string $attributeName the name of the attribute, which value should be slugified * @return string slugified value * @since 7.0.0 */ public function slugify($attributeName){ // toSlug should only work for existing attributes if(property_exists($this, $attributeName)){ $value = $this->$attributeName; // replace everything except alphanumeric with a single '-' $value = preg_replace('/[^A-Za-z0-9]+/', '-', $value); $value = strtolower($value); // trim '-' return trim($value, '-'); } else { throw new \BadFunctionCallException($attributeName . ' is not a valid attribute'); } } } public/AppFramework/Db/DoesNotExistException.php 0000604 00000002227 15247130450 0015725 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\AppFramework\Db; /** * This is returned or should be returned when a find request does not find an * entry in the database * @since 7.0.0 */ class DoesNotExistException extends \Exception { /** * Constructor * @param string $msg the error message * @since 7.0.0 */ public function __construct($msg){ parent::__construct($msg); } } public/AppFramework/Middleware.php 0000604 00000007363 15247130450 0013214 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\Middleware class */ namespace OCP\AppFramework; use OCP\AppFramework\Http\Response; /** * Middleware is used to provide hooks before or after controller methods and * deal with possible exceptions raised in the controller methods. * They're modeled after Django's middleware system: * https://docs.djangoproject.com/en/dev/topics/http/middleware/ * @since 6.0.0 */ abstract class Middleware { /** * This is being run in normal order before the controller is being * called which allows several modifications and checks * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @since 6.0.0 */ public function beforeController($controller, $methodName){ } /** * This is being run when either the beforeController method or the * controller method itself is throwing an exception. The middleware is * asked in reverse order to handle the exception and to return a response. * If the response is null, it is assumed that the exception could not be * handled and the error will be thrown again * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param \Exception $exception the thrown exception * @throws \Exception the passed in exception if it can't handle it * @return Response a Response object in case that the exception was handled * @since 6.0.0 */ public function afterException($controller, $methodName, \Exception $exception){ throw $exception; } /** * This is being run after a successful controllermethod call and allows * the manipulation of a Response object. The middleware is run in reverse order * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param Response $response the generated response from the controller * @return Response a Response object * @since 6.0.0 */ public function afterController($controller, $methodName, Response $response){ return $response; } /** * This is being run after the response object has been rendered and * allows the manipulation of the output. The middleware is run in reverse order * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param string $output the generated output from a response * @return string the output that should be printed * @since 6.0.0 */ public function beforeOutput($controller, $methodName, $output){ return $output; } } public/AppFramework/OCSController.php 0000604 00000007104 15247130450 0013620 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\Controller class */ namespace OCP\AppFramework; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; use OCP\IRequest; /** * Base class to inherit your controllers from that are used for RESTful APIs * @since 8.1.0 */ abstract class OCSController extends ApiController { /** @var int */ private $ocsVersion; /** * constructor of the controller * @param string $appName the name of the app * @param IRequest $request an instance of the request * @param string $corsMethods comma separated string of HTTP verbs which * should be allowed for websites or webapps when calling your API, defaults to * 'PUT, POST, GET, DELETE, PATCH' * @param string $corsAllowedHeaders comma separated string of HTTP headers * which should be allowed for websites or webapps when calling your API, * defaults to 'Authorization, Content-Type, Accept' * @param int $corsMaxAge number in seconds how long a preflighted OPTIONS * request should be cached, defaults to 1728000 seconds * @since 8.1.0 */ public function __construct($appName, IRequest $request, $corsMethods='PUT, POST, GET, DELETE, PATCH', $corsAllowedHeaders='Authorization, Content-Type, Accept', $corsMaxAge=1728000){ parent::__construct($appName, $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge); $this->registerResponder('json', function ($data) { return $this->buildOCSResponse('json', $data); }); $this->registerResponder('xml', function ($data) { return $this->buildOCSResponse('xml', $data); }); } /** * @param int $version * @since 11.0.0 * @internal */ public function setOCSVersion($version) { $this->ocsVersion = $version; } /** * Since the OCS endpoints default to XML we need to find out the format * again * @param mixed $response the value that was returned from a controller and * is not a Response instance * @param string $format the format for which a formatter has been registered * @throws \DomainException if format does not match a registered formatter * @return Response * @since 9.1.0 */ public function buildResponse($response, $format = 'xml') { return parent::buildResponse($response, $format); } /** * Unwrap data and build ocs response * @param string $format json or xml * @param DataResponse $data the data which should be transformed * @since 8.1.0 * @return \OC\AppFramework\OCS\BaseResponse */ private function buildOCSResponse($format, DataResponse $data) { if ($this->ocsVersion === 1) { return new \OC\AppFramework\OCS\V1Response($data, $format); } return new \OC\AppFramework\OCS\V2Response($data, $format); } } public/AppFramework/Http.php 0000604 00000006311 15247130450 0012046 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\HTTP class */ namespace OCP\AppFramework; /** * Base class which contains constants for HTTP status codes * @since 6.0.0 */ class Http { const STATUS_CONTINUE = 100; const STATUS_SWITCHING_PROTOCOLS = 101; const STATUS_PROCESSING = 102; const STATUS_OK = 200; const STATUS_CREATED = 201; const STATUS_ACCEPTED = 202; const STATUS_NON_AUTHORATIVE_INFORMATION = 203; const STATUS_NO_CONTENT = 204; const STATUS_RESET_CONTENT = 205; const STATUS_PARTIAL_CONTENT = 206; const STATUS_MULTI_STATUS = 207; const STATUS_ALREADY_REPORTED = 208; const STATUS_IM_USED = 226; const STATUS_MULTIPLE_CHOICES = 300; const STATUS_MOVED_PERMANENTLY = 301; const STATUS_FOUND = 302; const STATUS_SEE_OTHER = 303; const STATUS_NOT_MODIFIED = 304; const STATUS_USE_PROXY = 305; const STATUS_RESERVED = 306; const STATUS_TEMPORARY_REDIRECT = 307; const STATUS_BAD_REQUEST = 400; const STATUS_UNAUTHORIZED = 401; const STATUS_PAYMENT_REQUIRED = 402; const STATUS_FORBIDDEN = 403; const STATUS_NOT_FOUND = 404; const STATUS_METHOD_NOT_ALLOWED = 405; const STATUS_NOT_ACCEPTABLE = 406; const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; const STATUS_REQUEST_TIMEOUT = 408; const STATUS_CONFLICT = 409; const STATUS_GONE = 410; const STATUS_LENGTH_REQUIRED = 411; const STATUS_PRECONDITION_FAILED = 412; const STATUS_REQUEST_ENTITY_TOO_LARGE = 413; const STATUS_REQUEST_URI_TOO_LONG = 414; const STATUS_UNSUPPORTED_MEDIA_TYPE = 415; const STATUS_REQUEST_RANGE_NOT_SATISFIABLE = 416; const STATUS_EXPECTATION_FAILED = 417; const STATUS_IM_A_TEAPOT = 418; const STATUS_UNPROCESSABLE_ENTITY = 422; const STATUS_LOCKED = 423; const STATUS_FAILED_DEPENDENCY = 424; const STATUS_UPGRADE_REQUIRED = 426; const STATUS_PRECONDITION_REQUIRED = 428; const STATUS_TOO_MANY_REQUESTS = 429; const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; const STATUS_INTERNAL_SERVER_ERROR = 500; const STATUS_NOT_IMPLEMENTED = 501; const STATUS_BAD_GATEWAY = 502; const STATUS_SERVICE_UNAVAILABLE = 503; const STATUS_GATEWAY_TIMEOUT = 504; const STATUS_HTTP_VERSION_NOT_SUPPORTED = 505; const STATUS_VARIANT_ALSO_NEGOTIATES = 506; const STATUS_INSUFFICIENT_STORAGE = 507; const STATUS_LOOP_DETECTED = 508; const STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509; const STATUS_NOT_EXTENDED = 510; const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511; } public/AppFramework/Controller.php 0000604 00000016260 15247130450 0013256 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * AppFramework\Controller class */ namespace OCP\AppFramework; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; use OCP\IRequest; /** * Base class to inherit your controllers from * @since 6.0.0 */ abstract class Controller { /** * app name * @var string * @since 7.0.0 */ protected $appName; /** * current request * @var \OCP\IRequest * @since 6.0.0 */ protected $request; /** * @var array * @since 7.0.0 */ private $responders; /** * constructor of the controller * @param string $appName the name of the app * @param IRequest $request an instance of the request * @since 6.0.0 - parameter $appName was added in 7.0.0 - parameter $app was removed in 7.0.0 */ public function __construct($appName, IRequest $request) { $this->appName = $appName; $this->request = $request; // default responders $this->responders = array( 'json' => function ($data) { if ($data instanceof DataResponse) { $response = new JSONResponse( $data->getData(), $data->getStatus() ); $dataHeaders = $data->getHeaders(); $headers = $response->getHeaders(); // do not overwrite Content-Type if it already exists if (isset($dataHeaders['Content-Type'])) { unset($headers['Content-Type']); } $response->setHeaders(array_merge($dataHeaders, $headers)); return $response; } return new JSONResponse($data); } ); } /** * Parses an HTTP accept header and returns the supported responder type * @param string $acceptHeader * @return string the responder type * @since 7.0.0 * @since 9.1.0 Added default parameter */ public function getResponderByHTTPHeader($acceptHeader, $default='json') { $headers = explode(',', $acceptHeader); // return the first matching responder foreach ($headers as $header) { $header = strtolower(trim($header)); $responder = str_replace('application/', '', $header); if (array_key_exists($responder, $this->responders)) { return $responder; } } // no matching header return default return $default; } /** * Registers a formatter for a type * @param string $format * @param \Closure $responder * @since 7.0.0 */ protected function registerResponder($format, \Closure $responder) { $this->responders[$format] = $responder; } /** * Serializes and formats a response * @param mixed $response the value that was returned from a controller and * is not a Response instance * @param string $format the format for which a formatter has been registered * @throws \DomainException if format does not match a registered formatter * @return Response * @since 7.0.0 */ public function buildResponse($response, $format='json') { if(array_key_exists($format, $this->responders)) { $responder = $this->responders[$format]; return $responder($response); } throw new \DomainException('No responder registered for format '. $format . '!'); } /** * Lets you access post and get parameters by the index * @deprecated 7.0.0 write your parameters as method arguments instead * @param string $key the key which you want to access in the URL Parameter * placeholder, $_POST or $_GET array. * The priority how they're returned is the following: * 1. URL parameters * 2. POST parameters * 3. GET parameters * @param string $default If the key is not found, this value will be returned * @return mixed the content of the array * @since 6.0.0 */ public function params($key, $default=null){ return $this->request->getParam($key, $default); } /** * Returns all params that were received, be it from the request * (as GET or POST) or through the URL by the route * @deprecated 7.0.0 use $this->request instead * @return array the array with all parameters * @since 6.0.0 */ public function getParams() { return $this->request->getParams(); } /** * Returns the method of the request * @deprecated 7.0.0 use $this->request instead * @return string the method of the request (POST, GET, etc) * @since 6.0.0 */ public function method() { return $this->request->getMethod(); } /** * Shortcut for accessing an uploaded file through the $_FILES array * @deprecated 7.0.0 use $this->request instead * @param string $key the key that will be taken from the $_FILES array * @return array the file in the $_FILES element * @since 6.0.0 */ public function getUploadedFile($key) { return $this->request->getUploadedFile($key); } /** * Shortcut for getting env variables * @deprecated 7.0.0 use $this->request instead * @param string $key the key that will be taken from the $_ENV array * @return array the value in the $_ENV element * @since 6.0.0 */ public function env($key) { return $this->request->getEnv($key); } /** * Shortcut for getting cookie variables * @deprecated 7.0.0 use $this->request instead * @param string $key the key that will be taken from the $_COOKIE array * @return array the value in the $_COOKIE element * @since 6.0.0 */ public function cookie($key) { return $this->request->getCookie($key); } /** * Shortcut for rendering a template * @deprecated 7.0.0 return a template response instead * @param string $templateName the name of the template * @param array $params the template parameters in key => value structure * @param string $renderAs user renders a full page, blank only your template * admin an entry in the admin settings * @param string[] $headers set additional headers in name/value pairs * @return \OCP\AppFramework\Http\TemplateResponse containing the page * @since 6.0.0 */ public function render($templateName, array $params=array(), $renderAs='user', array $headers=array()){ $response = new TemplateResponse($this->appName, $templateName); $response->setParams($params); $response->renderAs($renderAs); foreach($headers as $name => $value){ $response->addHeader($name, $value); } return $response; } } public/Constants.php 0000604 00000004264 15247130450 0010512 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * This file defines common constants used in ownCloud */ namespace OCP; /** @deprecated 8.0.0 Use \OCP\Constants::PERMISSION_CREATE instead */ const PERMISSION_CREATE = 4; /** @deprecated 8.0.0 Use \OCP\Constants::PERMISSION_READ instead */ const PERMISSION_READ = 1; /** @deprecated 8.0.0 Use \OCP\Constants::PERMISSION_UPDATE instead */ const PERMISSION_UPDATE = 2; /** @deprecated 8.0.0 Use \OCP\Constants::PERMISSION_DELETE instead */ const PERMISSION_DELETE = 8; /** @deprecated 8.0.0 Use \OCP\Constants::PERMISSION_SHARE instead */ const PERMISSION_SHARE = 16; /** @deprecated 8.0.0 Use \OCP\Constants::PERMISSION_ALL instead */ const PERMISSION_ALL = 31; /** @deprecated 8.0.0 Use \OCP\Constants::FILENAME_INVALID_CHARS instead */ const FILENAME_INVALID_CHARS = "\\/<>:\"|?*\n"; /** * Class Constants * * @package OCP * @since 8.0.0 */ class Constants { /** * CRUDS permissions. * @since 8.0.0 */ const PERMISSION_CREATE = 4; const PERMISSION_READ = 1; const PERMISSION_UPDATE = 2; const PERMISSION_DELETE = 8; const PERMISSION_SHARE = 16; const PERMISSION_ALL = 31; /** * @since 8.0.0 - Updated in 9.0.0 to allow all POSIX chars since we no * longer support windows as server platform. */ const FILENAME_INVALID_CHARS = "\\/"; } public/OCS/IDiscoveryService.php 0000604 00000002421 15247130450 0012554 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\OCS; /** * Interface IDiscoveryService * * Allows you to discover OCS end-points on a remote server * * @package OCP\OCS * @since 12.0.0 */ interface IDiscoveryService { /** * Discover OCS end-points * * If no valid discovery data is found the defaults are returned * * @since 12.0.0 * * @param string $remote * @param string $service the service you want to discover * @return array */ public function discover($remote, $service); } public/Share_Backend.php 0000604 00000006554 15247130450 0011213 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Interface that apps must implement to share content. * @since 5.0.0 */ interface Share_Backend { /** * Check if this $itemSource exist for the user * @param string $itemSource * @param string $uidOwner Owner of the item * @return boolean|null Source * * Return false if the item does not exist for the user * @since 5.0.0 */ public function isValidSource($itemSource, $uidOwner); /** * Get a unique name of the item for the specified user * @param string $itemSource * @param string|false $shareWith User the item is being shared with * @param array|null $exclude List of similar item names already existing as shared items @deprecated since version OC7 * @return string Target name * * This function needs to verify that the user does not already have an item with this name. * If it does generate a new name e.g. name_# * @since 5.0.0 */ public function generateTarget($itemSource, $shareWith, $exclude = null); /** * Converts the shared item sources back into the item in the specified format * @param array $items Shared items * @param int $format * @return array * * The items array is a 3-dimensional array with the item_source as the * first key and the share id as the second key to an array with the share * info. * * The key/value pairs included in the share info depend on the function originally called: * If called by getItem(s)Shared: id, item_type, item, item_source, * share_type, share_with, permissions, stime, file_source * * If called by getItem(s)SharedWith: id, item_type, item, item_source, * item_target, share_type, share_with, permissions, stime, file_source, * file_target * * This function allows the backend to control the output of shared items with custom formats. * It is only called through calls to the public getItem(s)Shared(With) functions. * @since 5.0.0 */ public function formatItems($items, $format, $parameters = null); /** * Check if a given share type is allowd by the back-end * * @param int $shareType share type * @return boolean * * The back-end can enable/disable specific share types. Just return true if * the back-end doesn't provide any specific settings for it and want to allow * all share types defined by the share API * @since 8.0.0 */ public function isShareTypeAllowed($shareType); } public/ITempManager.php 0000604 00000003241 15247130450 0011041 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface ITempManager * * @package OCP * @since 8.0.0 */ interface ITempManager { /** * Create a temporary file and return the path * * @param string $postFix * @return string * @since 8.0.0 */ public function getTemporaryFile($postFix = ''); /** * Create a temporary folder and return the path * * @param string $postFix * @return string * @since 8.0.0 */ public function getTemporaryFolder($postFix = ''); /** * Remove the temporary files and folders generated during this request * @since 8.0.0 */ public function clean(); /** * Remove old temporary files and folders that were failed to be cleaned * @since 8.0.0 */ public function cleanOld(); /** * Get the temporary base directory * * @return string * @since 8.2.0 */ public function getTempBaseDir(); } public/ICacheFactory.php 0000604 00000002440 15247130450 0011174 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface ICacheFactory * * @package OCP * @since 7.0.0 */ interface ICacheFactory{ /** * Get a memory cache instance * * All entries added trough the cache instance will be namespaced by $prefix to prevent collisions between apps * * @param string $prefix * @return \OCP\ICache * @since 7.0.0 */ public function create($prefix = ''); /** * Check if any memory cache backend is available * * @return bool * @since 7.0.0 */ public function isAvailable(); } public/IAppConfig.php 0000604 00000006765 15247130450 0010525 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * This class provides an easy way for apps to store config values in the * database. * @since 7.0.0 */ interface IAppConfig { /** * check if a key is set in the appconfig * @param string $app * @param string $key * @return bool * @since 7.0.0 */ public function hasKey($app, $key); /** * Gets the config value * @param string $app app * @param string $key key * @param string $default = null, default value if the key does not exist * @return string the value or $default * @deprecated 8.0.0 use method getAppValue of \OCP\IConfig * * This function gets a value from the appconfig table. If the key does * not exist the default value will be returned * @since 7.0.0 */ public function getValue($app, $key, $default = null); /** * Deletes a key * @param string $app app * @param string $key key * @return bool * @deprecated 8.0.0 use method deleteAppValue of \OCP\IConfig * @since 7.0.0 */ public function deleteKey($app, $key); /** * Get the available keys for an app * @param string $app the app we are looking for * @return array an array of key names * @deprecated 8.0.0 use method getAppKeys of \OCP\IConfig * * This function gets all keys of an app. Please note that the values are * not returned. * @since 7.0.0 */ public function getKeys($app); /** * get multiply values, either the app or key can be used as wildcard by setting it to false * * @param string|false $key * @param string|false $app * @return array|false * @since 7.0.0 */ public function getValues($app, $key); /** * get all values of the app or and filters out sensitive data * * @param string $app * @return array * @since 12.0.0 */ public function getFilteredValues($app); /** * sets a value in the appconfig * @param string $app app * @param string $key key * @param string|float|int $value value * @deprecated 8.0.0 use method setAppValue of \OCP\IConfig * * Sets a value. If the key did not exist before it will be created. * @return void * @since 7.0.0 */ public function setValue($app, $key, $value); /** * Get all apps using the config * @return array an array of app ids * * This function returns a list of all apps that have at least one * entry in the appconfig table. * @since 7.0.0 */ public function getApps(); /** * Remove app from appconfig * @param string $app app * @return bool * @deprecated 8.0.0 use method deleteAppValue of \OCP\IConfig * * Removes all keys in appconfig belonging to the app. * @since 7.0.0 */ public function deleteApp($app); } public/Defaults.php 0000604 00000010761 15247130450 0010304 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author scolebrook <scolebrook@mac.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Defaults Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * public api to access default strings and urls for your templates * @since 6.0.0 */ class Defaults { /** * \OC_Defaults instance to retrieve the defaults * @since 6.0.0 */ private $defaults; /** * creates a \OC_Defaults instance which is used in all methods to retrieve the * actual defaults * @since 6.0.0 */ public function __construct(\OC_Defaults $defaults = null) { if ($defaults === null) { $defaults = \OC::$server->getThemingDefaults(); } $this->defaults = $defaults; } /** * get base URL for the organisation behind your ownCloud instance * @return string * @since 6.0.0 */ public function getBaseUrl() { return $this->defaults->getBaseUrl(); } /** * link to the desktop sync client * @return string * @since 6.0.0 */ public function getSyncClientUrl() { return $this->defaults->getSyncClientUrl(); } /** * link to the iOS client * @return string * @since 8.0.0 */ public function getiOSClientUrl() { return $this->defaults->getiOSClientUrl(); } /** * link to the Android client * @return string * @since 8.0.0 */ public function getAndroidClientUrl() { return $this->defaults->getAndroidClientUrl(); } /** * base URL to the documentation of your ownCloud instance * @return string * @since 6.0.0 */ public function getDocBaseUrl() { return $this->defaults->getDocBaseUrl(); } /** * name of your ownCloud instance * @return string * @since 6.0.0 */ public function getName() { return $this->defaults->getName(); } /** * name of your ownCloud instance containing HTML styles * @return string * @since 8.0.0 */ public function getHTMLName() { return $this->defaults->getHTMLName(); } /** * Entity behind your onwCloud instance * @return string * @since 6.0.0 */ public function getEntity() { return $this->defaults->getEntity(); } /** * ownCloud slogan * @return string * @since 6.0.0 */ public function getSlogan() { return $this->defaults->getSlogan(); } /** * logo claim * @return string * @since 6.0.0 */ public function getLogoClaim() { return $this->defaults->getLogoClaim(); } /** * footer, short version * @return string * @since 6.0.0 */ public function getShortFooter() { return $this->defaults->getShortFooter(); } /** * footer, long version * @return string * @since 6.0.0 */ public function getLongFooter() { return $this->defaults->getLongFooter(); } /** * Returns the AppId for the App Store for the iOS Client * @return string AppId * @since 8.0.0 */ public function getiTunesAppId() { return $this->defaults->getiTunesAppId(); } /** * Themed logo url * * @param bool $useSvg Whether to point to the SVG image or a fallback * @return string * @since 12.0.0 */ public function getLogo($useSvg = true) { return $this->defaults->getLogo($useSvg); } /** * Returns primary color * @return string * @since 12.0.0 */ public function getColorPrimary() { return $this->defaults->getColorPrimary(); } /** * @param string $key * @return string URL to doc with key * @since 12.0.0 */ public function buildDocLinkToKey($key) { return $this->defaults->buildDocLinkToKey($key); } /** * Returns the title * @return string title * @since 12.0.0 */ public function getTitle() { return $this->defaults->getTitle(); } } public/RichObjectStrings/Definitions.php 0000604 00000025764 15247130450 0014407 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\RichObjectStrings; /** * Class Definitions * * @package OCP\RichObjectStrings * @since 11.0.0 */ class Definitions { /** * @var array * @since 11.0.0 */ public $definitions = [ 'addressbook' => [ 'author' => 'Nextcloud', 'app' => 'dav', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the addressbook on the instance', 'example' => '42', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The display name of the addressbook which should be used in the visual representation', 'example' => 'Contacts', ], ], ], 'addressbook-contact' => [ 'author' => 'Nextcloud', 'app' => 'dav', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the contact on the instance', 'example' => '42', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The display name of the contact which should be used in the visual representation', 'example' => 'John Doe', ], ], ], 'announcement' => [ 'author' => 'Joas Schilling', 'app' => 'announcementcenter', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the announcement on the instance', 'example' => '42', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The announcement subject which should be used in the visual representation', 'example' => 'file.txt', ], 'link' => [ 'since' => '11.0.0', 'required' => false, 'description' => 'The full URL to the file', 'example' => 'http://localhost/index.php/apps/announcements/#23', ], ], ], 'app' => [ 'author' => 'Nextcloud', 'app' => 'updatenotification', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The app id', 'example' => 'updatenotification', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The name of the app which should be used in the visual representation', 'example' => 'Update notification', ], ], ], 'calendar' => [ 'author' => 'Nextcloud', 'app' => 'dav', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the calendar on the instance', 'example' => '42', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The display name of the calendar which should be used in the visual representation', 'example' => 'Personal', ], ], ], 'calendar-event' => [ 'author' => 'Nextcloud', 'app' => 'dav', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the event on the instance', 'example' => '42', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The display name of the event which should be used in the visual representation', 'example' => 'Workout', ], ], ], 'call' => [ 'author' => 'Nextcloud', 'app' => 'spreed', 'since' => '11.0.2', 'parameters' => [ 'id' => [ 'since' => '11.0.2', 'required' => true, 'description' => 'The id used to identify the call on the instance', 'example' => '42', ], 'name' => [ 'since' => '11.0.2', 'required' => true, 'description' => 'The display name of the call which should be used in the visual representation', 'example' => 'Company call', ], 'call-type' => [ 'since' => '11.0.2', 'required' => true, 'description' => 'The type of the call: one2one, group or public', 'example' => 'one2one', ], ], ], 'circle' => [ 'author' => 'Maxence Lange', 'app' => 'circles', 'since' => '12.0.0', 'parameters' => [ 'id' => [ 'since' => '12.0.0', 'required' => true, 'description' => 'The id used to identify the circle on the instance', 'example' => '42', ], 'name' => [ 'since' => '12.0.0', 'required' => true, 'description' => 'The display name of the circle which should be used in the visual representation', 'example' => 'My friends', ], 'link' => [ 'since' => '12.0.0', 'required' => true, 'description' => 'The full URL to the circle', 'example' => 'http://localhost/index.php/apps/circles/#42', ], ], ], 'email' => [ 'author' => 'Nextcloud', 'app' => 'sharebymail', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The mail-address used to identify the event on the instance', 'example' => 'test@localhost', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The display name of a matching contact or the email (fallback) which should be used in the visual representation', 'example' => 'Foo Bar', ], ], ], 'file' => [ 'author' => 'Nextcloud', 'app' => 'dav', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the file on the instance', 'example' => '42', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The file name which should be used in the visual representation', 'example' => 'file.txt', ], 'path' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The full path of the file for the user, should not start with a slash', 'example' => 'path/to/file.txt', ], 'link' => [ 'since' => '11.0.0', 'required' => false, 'description' => 'The full URL to the file', 'example' => 'http://localhost/index.php/f/42', ], ], ], 'open-graph' => [ 'author' => 'Maxence Lange', 'app' => 'mood', 'since' => '12.0.0', 'parameters' => [ 'id' => [ 'since' => '12.0.0', 'required' => true, 'description' => 'The id used to identify the open graph data on the instance', 'example' => '42', ], 'name' => [ 'since' => '12.0.0', 'required' => true, 'description' => 'The open graph title of the website', 'example' => 'This is a website', ], 'description' => [ 'since' => '12.0.0', 'required' => false, 'description' => 'The open graph description from the website', 'example' => 'This is the description of the website', ], 'thumb' => [ 'since' => '12.0.0', 'required' => false, 'description' => 'The full URL of the open graph thumbnail', 'example' => 'http://localhost/index.php/apps/mood/data/image?url=https%3A%2F%2Fthumb.example.com%2Fimage.png', ], 'website' => [ 'since' => '12.0.0', 'required' => false, 'description' => 'The name of the described website', 'example' => 'Nextcloud - App Store', ], 'link' => [ 'since' => '12.0.0', 'required' => false, 'description' => 'The full link to the website', 'example' => 'https://apps.nextcloud.com/apps/mood', ], ], ], 'pending-federated-share' => [ 'author' => 'Nextcloud', 'app' => 'dav', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the federated share on the instance', 'example' => '42', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The name of the shared item which should be used in the visual representation', 'example' => 'file.txt', ], ], ], 'systemtag' => [ 'author' => 'Nextcloud', 'app' => 'core', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the systemtag on the instance', 'example' => '23', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The display name of the systemtag which should be used in the visual representation', 'example' => 'Project 1', ], 'visibility' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'If the user can see the systemtag', 'example' => '1', ], 'assignable' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'If the user can assign the systemtag', 'example' => '0', ], ], ], 'user' => [ 'author' => 'Nextcloud', 'app' => 'core', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the user on the instance', 'example' => 'johndoe', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The display name of the user which should be used in the visual representation', 'example' => 'John Doe', ], 'server' => [ 'since' => '11.0.0', 'required' => false, 'description' => 'The URL of the instance the user lives on', 'example' => 'localhost', ], ], ], 'user-group' => [ 'author' => 'Nextcloud', 'app' => 'core', 'since' => '11.0.0', 'parameters' => [ 'id' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The id used to identify the group on the instance', 'example' => 'supportteam', ], 'name' => [ 'since' => '11.0.0', 'required' => true, 'description' => 'The display name of the group which should be used in the visual representation', 'example' => 'Support Team', ], ], ], ]; /** * @param string $type * @return array * @throws InvalidObjectExeption * @since 11.0.0 */ public function getDefinition($type) { if (isset($this->definitions[$type])) { return $this->definitions[$type]; } throw new InvalidObjectExeption('Object type is undefined'); } } public/RichObjectStrings/IValidator.php 0000604 00000002152 15247130450 0014154 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\RichObjectStrings; /** * Class Validator * * @package OCP\RichObjectStrings * @since 11.0.0 */ interface IValidator { /** * @param string $subject * @param array[] $parameters * @throws InvalidObjectExeption * @since 11.0.0 */ public function validate($subject, array $parameters); } public/RichObjectStrings/InvalidObjectExeption.php 0000604 00000001756 15247130450 0016360 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\RichObjectStrings; /** * Class InvalidObjectExeption * * @package OCP\RichObjectStrings * @since 11.0.0 */ class InvalidObjectExeption extends \InvalidArgumentException { } public/BackgroundJob/IJobList.php 0000604 00000006037 15247130450 0012727 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\BackgroundJob; /** * Interface IJobList * * @package OCP\BackgroundJob * @since 7.0.0 */ interface IJobList { /** * Add a job to the list * * @param \OCP\BackgroundJob\IJob|string $job * @param mixed $argument The argument to be passed to $job->run() when the job is exectured * @since 7.0.0 */ public function add($job, $argument = null); /** * Remove a job from the list * * @param \OCP\BackgroundJob\IJob|string $job * @param mixed $argument * @since 7.0.0 */ public function remove($job, $argument = null); /** * check if a job is in the list * * @param \OCP\BackgroundJob\IJob|string $job * @param mixed $argument * @return bool * @since 7.0.0 */ public function has($job, $argument); /** * get all jobs in the list * * @return \OCP\BackgroundJob\IJob[] * @since 7.0.0 * @deprecated 9.0.0 - This method is dangerous since it can cause load and * memory problems when creating too many instances. */ public function getAll(); /** * get the next job in the list * * @return \OCP\BackgroundJob\IJob|null * @since 7.0.0 */ public function getNext(); /** * @param int $id * @return \OCP\BackgroundJob\IJob|null * @since 7.0.0 */ public function getById($id); /** * set the job that was last ran to the current time * * @param \OCP\BackgroundJob\IJob $job * @since 7.0.0 */ public function setLastJob(IJob $job); /** * Remove the reservation for a job * * @param IJob $job * @since 9.1.0 */ public function unlockJob(IJob $job); /** * get the id of the last ran job * * @return int * @since 7.0.0 * @deprecated 9.1.0 - The functionality behind the value is deprecated, it * only tells you which job finished last, but since we now allow multiple * executors to run in parallel, it's not used to calculate the next job. */ public function getLastJob(); /** * set the lastRun of $job to now * * @param IJob $job * @since 7.0.0 */ public function setLastRun(IJob $job); /** * set the run duration of $job * * @param IJob $job * @param $timeTaken * @since 12.0.0 */ public function setExecutionTime(IJob $job, $timeTaken); } public/BackgroundJob/IJob.php 0000604 00000003776 15247130450 0012102 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\BackgroundJob; use OCP\ILogger; /** * Interface IJob * * @package OCP\BackgroundJob * @since 7.0.0 */ interface IJob { /** * Run the background job with the registered argument * * @param \OCP\BackgroundJob\IJobList $jobList The job list that manages the state of this job * @param ILogger $logger * @since 7.0.0 */ public function execute($jobList, ILogger $logger = null); /** * @param int $id * @since 7.0.0 */ public function setId($id); /** * @param int $lastRun * @since 7.0.0 */ public function setLastRun($lastRun); /** * @param mixed $argument * @since 7.0.0 */ public function setArgument($argument); /** * Get the id of the background job * This id is determined by the job list when a job is added to the list * * @return int * @since 7.0.0 */ public function getId(); /** * Get the last time this job was run as unix timestamp * * @return int * @since 7.0.0 */ public function getLastRun(); /** * Get the argument associated with the background job * This is the argument that will be passed to the background job * * @return mixed * @since 7.0.0 */ public function getArgument(); } public/IServerContainer.php 0000604 00000025445 15247130450 0011764 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Server container interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; use OCP\Security\IContentSecurityPolicyManager; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class IServerContainer * @package OCP * * This container holds all ownCloud services * @since 6.0.0 */ interface IServerContainer extends IContainer { /** * The contacts manager will act as a broker between consumers for contacts information and * providers which actual deliver the contact information. * * @return \OCP\Contacts\IManager * @since 6.0.0 */ public function getContactsManager(); /** * The current request object holding all information about the request currently being processed * is returned from this method. * In case the current execution was not initiated by a web request null is returned * * @return \OCP\IRequest * @since 6.0.0 */ public function getRequest(); /** * Returns the preview manager which can create preview images for a given file * * @return \OCP\IPreview * @since 6.0.0 */ public function getPreviewManager(); /** * Returns the tag manager which can get and set tags for different object types * * @see \OCP\ITagManager::load() * @return \OCP\ITagManager * @since 6.0.0 */ public function getTagManager(); /** * Returns the root folder of ownCloud's data directory * * @return \OCP\Files\IRootFolder * @since 6.0.0 - between 6.0.0 and 8.0.0 this returned \OCP\Files\Folder */ public function getRootFolder(); /** * Returns a view to ownCloud's files folder * * @param string $userId user ID * @return \OCP\Files\Folder * @since 6.0.0 - parameter $userId was added in 8.0.0 * @see getUserFolder in \OCP\Files\IRootFolder */ public function getUserFolder($userId = null); /** * Returns an app-specific view in ownClouds data directory * * @return \OCP\Files\Folder * @since 6.0.0 * @deprecated since 9.2.0 use IAppData */ public function getAppFolder(); /** * Returns a user manager * * @return \OCP\IUserManager * @since 8.0.0 */ public function getUserManager(); /** * Returns a group manager * * @return \OCP\IGroupManager * @since 8.0.0 */ public function getGroupManager(); /** * Returns the user session * * @return \OCP\IUserSession * @since 6.0.0 */ public function getUserSession(); /** * Returns the navigation manager * * @return \OCP\INavigationManager * @since 6.0.0 */ public function getNavigationManager(); /** * Returns the config manager * * @return \OCP\IConfig * @since 6.0.0 */ public function getConfig(); /** * Returns a Crypto instance * * @return \OCP\Security\ICrypto * @since 8.0.0 */ public function getCrypto(); /** * Returns a Hasher instance * * @return \OCP\Security\IHasher * @since 8.0.0 */ public function getHasher(); /** * Returns a SecureRandom instance * * @return \OCP\Security\ISecureRandom * @since 8.1.0 */ public function getSecureRandom(); /** * Returns a CredentialsManager instance * * @return \OCP\Security\ICredentialsManager * @since 9.0.0 */ public function getCredentialsManager(); /** * Returns the app config manager * * @return \OCP\IAppConfig * @since 7.0.0 */ public function getAppConfig(); /** * @return \OCP\L10N\IFactory * @since 8.2.0 */ public function getL10NFactory(); /** * get an L10N instance * @param string $app appid * @param string $lang * @return \OCP\IL10N * @since 6.0.0 - parameter $lang was added in 8.0.0 */ public function getL10N($app, $lang = null); /** * @return \OC\Encryption\Manager * @since 8.1.0 */ public function getEncryptionManager(); /** * @return \OC\Encryption\File * @since 8.1.0 */ public function getEncryptionFilesHelper(); /** * @return \OCP\Encryption\Keys\IStorage * @since 8.1.0 */ public function getEncryptionKeyStorage(); /** * Returns the URL generator * * @return \OCP\IURLGenerator * @since 6.0.0 */ public function getURLGenerator(); /** * Returns the Helper * * @return \OCP\IHelper * @since 6.0.0 */ public function getHelper(); /** * Returns an ICache instance * * @return \OCP\ICache * @since 6.0.0 */ public function getCache(); /** * Returns an \OCP\CacheFactory instance * * @return \OCP\ICacheFactory * @since 7.0.0 */ public function getMemCacheFactory(); /** * Returns the current session * * @return \OCP\ISession * @since 6.0.0 */ public function getSession(); /** * Returns the activity manager * * @return \OCP\Activity\IManager * @since 6.0.0 */ public function getActivityManager(); /** * Returns the current session * * @return \OCP\IDBConnection * @since 6.0.0 */ public function getDatabaseConnection(); /** * Returns an avatar manager, used for avatar functionality * * @return \OCP\IAvatarManager * @since 6.0.0 */ public function getAvatarManager(); /** * Returns an job list for controlling background jobs * * @return \OCP\BackgroundJob\IJobList * @since 7.0.0 */ public function getJobList(); /** * Returns a logger instance * * @return \OCP\ILogger * @since 8.0.0 */ public function getLogger(); /** * Returns a router for generating and matching urls * * @return \OCP\Route\IRouter * @since 7.0.0 */ public function getRouter(); /** * Returns a search instance * * @return \OCP\ISearch * @since 7.0.0 */ public function getSearch(); /** * Get the certificate manager for the user * * @param string $userId (optional) if not specified the current loggedin user is used, use null to get the system certificate manager * @return \OCP\ICertificateManager | null if $userId is null and no user is logged in * @since 8.0.0 */ public function getCertificateManager($userId = null); /** * Create a new event source * * @return \OCP\IEventSource * @since 8.0.0 */ public function createEventSource(); /** * Returns an instance of the HTTP helper class * @return \OC\HTTPHelper * @deprecated 8.1.0 Use \OCP\Http\Client\IClientService * @since 8.0.0 */ public function getHTTPHelper(); /** * Returns an instance of the HTTP client service * * @return \OCP\Http\Client\IClientService * @since 8.1.0 */ public function getHTTPClientService(); /** * Get the active event logger * * @return \OCP\Diagnostics\IEventLogger * @since 8.0.0 */ public function getEventLogger(); /** * Get the active query logger * * The returned logger only logs data when debug mode is enabled * * @return \OCP\Diagnostics\IQueryLogger * @since 8.0.0 */ public function getQueryLogger(); /** * Get the manager for temporary files and folders * * @return \OCP\ITempManager * @since 8.0.0 */ public function getTempManager(); /** * Get the app manager * * @return \OCP\App\IAppManager * @since 8.0.0 */ public function getAppManager(); /** * Get the webroot * * @return string * @since 8.0.0 */ public function getWebRoot(); /** * @return \OCP\Files\Config\IMountProviderCollection * @since 8.0.0 */ public function getMountProviderCollection(); /** * Get the IniWrapper * * @return \bantu\IniGetWrapper\IniGetWrapper * @since 8.0.0 */ public function getIniWrapper(); /** * @return \OCP\Command\IBus * @since 8.1.0 */ public function getCommandBus(); /** * Creates a new mailer * * @return \OCP\Mail\IMailer * @since 8.1.0 */ public function getMailer(); /** * Get the locking provider * * @return \OCP\Lock\ILockingProvider * @since 8.1.0 */ public function getLockingProvider(); /** * @return \OCP\Files\Mount\IMountManager * @since 8.2.0 */ public function getMountManager(); /** * Get the MimeTypeDetector * * @return \OCP\Files\IMimeTypeDetector * @since 8.2.0 */ public function getMimeTypeDetector(); /** * Get the MimeTypeLoader * * @return \OCP\Files\IMimeTypeLoader * @since 8.2.0 */ public function getMimeTypeLoader(); /** * Get the EventDispatcher * * @return EventDispatcherInterface * @since 8.2.0 */ public function getEventDispatcher(); /** * Get the Notification Manager * * @return \OCP\Notification\IManager * @since 9.0.0 */ public function getNotificationManager(); /** * @return \OCP\Comments\ICommentsManager * @since 9.0.0 */ public function getCommentsManager(); /** * Returns the system-tag manager * * @return \OCP\SystemTag\ISystemTagManager * * @since 9.0.0 */ public function getSystemTagManager(); /** * Returns the system-tag object mapper * * @return \OCP\SystemTag\ISystemTagObjectMapper * * @since 9.0.0 */ public function getSystemTagObjectMapper(); /** * Returns the share manager * * @return \OCP\Share\IManager * @since 9.0.0 */ public function getShareManager(); /** * @return IContentSecurityPolicyManager * @since 9.0.0 */ public function getContentSecurityPolicyManager(); /** * @return \OCP\IDateTimeZone * @since 8.0.0 */ public function getDateTimeZone(); /** * @return \OCP\IDateTimeFormatter * @since 8.0.0 */ public function getDateTimeFormatter(); /** * @return \OCP\Federation\ICloudIdManager * @since 12.0.0 */ public function getCloudIdManager(); } public/IGroupManager.php 0000604 00000006547 15247130450 0011244 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Class Manager * * Hooks available in scope \OC\Group: * - preAddUser(\OC\Group\Group $group, \OC\User\User $user) * - postAddUser(\OC\Group\Group $group, \OC\User\User $user) * - preRemoveUser(\OC\Group\Group $group, \OC\User\User $user) * - postRemoveUser(\OC\Group\Group $group, \OC\User\User $user) * - preDelete(\OC\Group\Group $group) * - postDelete(\OC\Group\Group $group) * - preCreate(string $groupId) * - postCreate(\OC\Group\Group $group) * * @package OC\Group * @since 8.0.0 */ interface IGroupManager { /** * Checks whether a given backend is used * * @param string $backendClass Full classname including complete namespace * @return bool * @since 8.1.0 */ public function isBackendUsed($backendClass); /** * @param \OCP\GroupInterface $backend * @since 8.0.0 */ public function addBackend($backend); /** * @since 8.0.0 */ public function clearBackends(); /** * @param string $gid * @return \OCP\IGroup * @since 8.0.0 */ public function get($gid); /** * @param string $gid * @return bool * @since 8.0.0 */ public function groupExists($gid); /** * @param string $gid * @return \OCP\IGroup * @since 8.0.0 */ public function createGroup($gid); /** * @param string $search * @param int $limit * @param int $offset * @return \OCP\IGroup[] * @since 8.0.0 */ public function search($search, $limit = null, $offset = null); /** * @param \OCP\IUser|null $user * @return \OCP\IGroup[] * @since 8.0.0 */ public function getUserGroups($user); /** * @param \OCP\IUser $user * @return array with group names * @since 8.0.0 */ public function getUserGroupIds($user); /** * get a list of all display names in a group * * @param string $gid * @param string $search * @param int $limit * @param int $offset * @return array an array of display names (value) and user ids (key) * @since 8.0.0 */ public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0); /** * Checks if a userId is in the admin group * @param string $userId * @return bool if admin * @since 8.0.0 */ public function isAdmin($userId); /** * Checks if a userId is in a group * @param string $userId * @param string $group * @return bool if in group * @since 8.0.0 */ public function isInGroup($userId, $group); } public/SystemTag/ISystemTag.php 0000604 00000002727 15247130450 0012511 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; /** * Public interface for a system-wide tag. * * @since 9.0.0 */ interface ISystemTag { /** * Returns the tag id * * @return string id * * @since 9.0.0 */ public function getId(); /** * Returns the tag display name * * @return string tag display name * * @since 9.0.0 */ public function getName(); /** * Returns whether the tag is visible for regular users * * @return bool true if visible, false otherwise * * @since 9.0.0 */ public function isUserVisible(); /** * Returns whether the tag can be assigned to objects by regular users * * @return bool true if assignable, false otherwise * * @since 9.0.0 */ public function isUserAssignable(); } public/SystemTag/ManagerEvent.php 0000604 00000004005 15247130450 0013023 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; use Symfony\Component\EventDispatcher\Event; /** * Class ManagerEvent * * @package OCP\SystemTag * @since 9.0.0 */ class ManagerEvent extends Event { const EVENT_CREATE = 'OCP\SystemTag\ISystemTagManager::createTag'; const EVENT_UPDATE = 'OCP\SystemTag\ISystemTagManager::updateTag'; const EVENT_DELETE = 'OCP\SystemTag\ISystemTagManager::deleteTag'; /** @var string */ protected $event; /** @var ISystemTag */ protected $tag; /** @var ISystemTag */ protected $beforeTag; /** * DispatcherEvent constructor. * * @param string $event * @param ISystemTag $tag * @param ISystemTag $beforeTag * @since 9.0.0 */ public function __construct($event, ISystemTag $tag, ISystemTag $beforeTag = null) { $this->event = $event; $this->tag = $tag; $this->beforeTag = $beforeTag; } /** * @return string * @since 9.0.0 */ public function getEvent() { return $this->event; } /** * @return ISystemTag * @since 9.0.0 */ public function getTag() { return $this->tag; } /** * @return ISystemTag * @since 9.0.0 */ public function getTagBefore() { if ($this->event !== self::EVENT_UPDATE) { throw new \BadMethodCallException('getTagBefore is only available on the update Event'); } return $this->beforeTag; } } public/SystemTag/MapperEvent.php 0000604 00000003773 15247130450 0012710 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; use Symfony\Component\EventDispatcher\Event; /** * Class MapperEvent * * @package OCP\SystemTag * @since 9.0.0 */ class MapperEvent extends Event { const EVENT_ASSIGN = 'OCP\SystemTag\ISystemTagObjectMapper::assignTags'; const EVENT_UNASSIGN = 'OCP\SystemTag\ISystemTagObjectMapper::unassignTags'; /** @var string */ protected $event; /** @var string */ protected $objectType; /** @var string */ protected $objectId; /** @var int[] */ protected $tags; /** * DispatcherEvent constructor. * * @param string $event * @param string $objectType * @param string $objectId * @param int[] $tags * @since 9.0.0 */ public function __construct($event, $objectType, $objectId, array $tags) { $this->event = $event; $this->objectType = $objectType; $this->objectId = $objectId; $this->tags = $tags; } /** * @return string * @since 9.0.0 */ public function getEvent() { return $this->event; } /** * @return string * @since 9.0.0 */ public function getObjectType() { return $this->objectType; } /** * @return string * @since 9.0.0 */ public function getObjectId() { return $this->objectId; } /** * @return int[] * @since 9.0.0 */ public function getTags() { return $this->tags; } } public/SystemTag/SystemTagsEntityEvent.php 0000604 00000004073 15247130450 0014756 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; use Symfony\Component\EventDispatcher\Event; /** * Class SystemTagsEntityEvent * * @package OCP\SystemTag * @since 9.1.0 */ class SystemTagsEntityEvent extends Event { const EVENT_ENTITY = 'OCP\SystemTag\ISystemTagManager::registerEntity'; /** @var string */ protected $event; /** @var \Closure[] */ protected $collections; /** * SystemTagsEntityEvent constructor. * * @param string $event * @since 9.1.0 */ public function __construct($event) { $this->event = $event; $this->collections = []; } /** * @param string $name * @param \Closure $entityExistsFunction The closure should take one * argument, which is the id of the entity, that tags * should be handled for. The return should then be bool, * depending on whether tags are allowed (true) or not. * @throws \OutOfBoundsException when the entity name is already taken * @since 9.1.0 */ public function addEntityCollection($name, \Closure $entityExistsFunction) { if (isset($this->collections[$name])) { throw new \OutOfBoundsException('Duplicate entity name "' . $name . '"'); } $this->collections[$name] = $entityExistsFunction; } /** * @return \Closure[] * @since 9.1.0 */ public function getEntityCollections() { return $this->collections; } } public/SystemTag/ISystemTagManager.php 0000604 00000011630 15247130450 0013775 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; use OCP\IUser; /** * Public interface to access and manage system-wide tags. * * @since 9.0.0 */ interface ISystemTagManager { /** * Returns the tag objects matching the given tag ids. * * @param array|string $tagIds id or array of unique ids of the tag to retrieve * * @return \OCP\SystemTag\ISystemTag[] array of system tags with tag id as key * * @throws \InvalidArgumentException if at least one given tag ids is invalid (string instead of integer, etc.) * @throws \OCP\SystemTag\TagNotFoundException if at least one given tag ids did no exist * The message contains a json_encoded array of the ids that could not be found * * @since 9.0.0 */ public function getTagsByIds($tagIds); /** * Returns the tag object matching the given attributes. * * @param string $tagName tag name * @param bool $userVisible whether the tag is visible by users * @param bool $userAssignable whether the tag is assignable by users * * @return \OCP\SystemTag\ISystemTag system tag * * @throws \OCP\SystemTag\TagNotFoundException if tag does not exist * * @since 9.0.0 */ public function getTag($tagName, $userVisible, $userAssignable); /** * Creates the tag object using the given attributes. * * @param string $tagName tag name * @param bool $userVisible whether the tag is visible by users * @param bool $userAssignable whether the tag is assignable by users * * @return \OCP\SystemTag\ISystemTag system tag * * @throws \OCP\SystemTag\TagAlreadyExistsException if tag already exists * * @since 9.0.0 */ public function createTag($tagName, $userVisible, $userAssignable); /** * Returns all known tags, optionally filtered by visibility. * * @param bool|null $visibilityFilter filter by visibility if non-null * @param string $nameSearchPattern optional search pattern for the tag name * * @return \OCP\SystemTag\ISystemTag[] array of system tags or empty array if none found * * @since 9.0.0 */ public function getAllTags($visibilityFilter = null, $nameSearchPattern = null); /** * Updates the given tag * * @param string $tagId tag id * @param string $newName the new tag name * @param bool $userVisible whether the tag is visible by users * @param bool $userAssignable whether the tag is assignable by users * * @throws \OCP\SystemTag\TagNotFoundException if tag with the given id does not exist * @throws \OCP\SystemTag\TagAlreadyExistsException if there is already another tag * with the same attributes * * @since 9.0.0 */ public function updateTag($tagId, $newName, $userVisible, $userAssignable); /** * Delete the given tags from the database and all their relationships. * * @param string|array $tagIds array of tag ids * * @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist * * @since 9.0.0 */ public function deleteTags($tagIds); /** * Checks whether the given user is allowed to assign/unassign the tag with the * given id. * * @param ISystemTag $tag tag to check permission for * @param IUser $user user to check permission for * * @return true if the user is allowed to assign/unassign the tag, false otherwise * * @since 9.1.0 */ public function canUserAssignTag(ISystemTag $tag, IUser $user); /** * Checks whether the given user is allowed to see the tag with the given id. * * @param ISystemTag $tag tag to check permission for * @param IUser $user user to check permission for * * @return true if the user can see the tag, false otherwise * * @since 9.1.0 */ public function canUserSeeTag(ISystemTag $tag, IUser $userId); /** * Set groups that can assign a given tag. * * @param ISystemTag $tag tag for group assignment * @param string[] $groupIds group ids of groups that can assign/unassign the tag * * @since 9.1.0 */ public function setTagGroups(ISystemTag $tag, $groupIds); /** * Get groups that can assign a given tag. * * @param ISystemTag $tag tag for group assignment * * @return string[] group ids of groups that can assign/unassign the tag * * @since 9.1.0 */ public function getTagGroups(ISystemTag $tag); } public/SystemTag/TagAlreadyExistsException.php 0000604 00000001617 15247130450 0015551 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; /** * Exception when a tag already exists. * * @since 9.0.0 */ class TagAlreadyExistsException extends \RuntimeException {} public/SystemTag/ISystemTagObjectMapper.php 0000604 00000007475 15247130450 0015012 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; /** * Public interface to access and manage system-wide tags. * * @since 9.0.0 */ interface ISystemTagObjectMapper { /** * Get a list of tag ids for the given object ids. * * This returns an array that maps object id to tag ids * [ * 1 => array('id1', 'id2'), * 2 => array('id3', 'id2'), * 3 => array('id5'), * 4 => array() * ] * * Untagged objects will have an empty array associated. * * @param string|array $objIds object ids * @param string $objectType object type * * @return array with object id as key and an array * of tag ids as value * * @since 9.0.0 */ public function getTagIdsForObjects($objIds, $objectType); /** * Get a list of objects tagged with $tagIds. * * @param string|array $tagIds Tag id or array of tag ids. * @param string $objectType object type * @param int $limit Count of object ids you want to get * @param string $offset The last object id you already received * * @return string[] array of object ids or empty array if none found * * @throws \OCP\SystemTag\TagNotFoundException if at least one of the * given tags does not exist * @throws \InvalidArgumentException When a limit is specified together with * multiple tag ids * * @since 9.0.0 */ public function getObjectIdsForTags($tagIds, $objectType, $limit = 0, $offset = ''); /** * Assign the given tags to the given object. * * If at least one of the given tag ids doesn't exist, none of the tags * will be assigned. * * If the relationship already existed, fail silently. * * @param string $objId object id * @param string $objectType object type * @param string|array $tagIds tag id or array of tag ids to assign * * @throws \OCP\SystemTag\TagNotFoundException if at least one of the * given tags does not exist * * @since 9.0.0 */ public function assignTags($objId, $objectType, $tagIds); /** * Unassign the given tags from the given object. * * If at least one of the given tag ids doesn't exist, none of the tags * will be unassigned. * * If the relationship did not exist in the first place, fail silently. * * @param string $objId object id * @param string $objectType object type * @param string|array $tagIds tag id or array of tag ids to unassign * * @throws \OCP\SystemTag\TagNotFoundException if at least one of the * given tags does not exist * * @since 9.0.0 */ public function unassignTags($objId, $objectType, $tagIds); /** * Checks whether the given objects have the given tag. * * @param string|array $objIds object ids * @param string $objectType object type * @param string $tagId tag id to check * @param bool $all true to check that ALL objects have the tag assigned, * false to check that at least ONE object has the tag. * * @return bool true if the condition set by $all is matched, false * otherwise * * @throws \OCP\SystemTag\TagNotFoundException if the tag does not exist * * @since 9.0.0 */ public function haveTag($objIds, $objectType, $tagId, $all = true); } public/SystemTag/TagNotFoundException.php 0000604 00000002666 15247130450 0014531 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; /** * Exception when a tag was not found. * * @since 9.0.0 */ class TagNotFoundException extends \RuntimeException { /** @var string[] */ protected $tags; /** * TagNotFoundException constructor. * * @param string $message * @param int $code * @param \Exception $previous * @param string[] $tags * @since 9.0.0 */ public function __construct($message = '', $code = 0, \Exception $previous = null, array $tags = []) { parent::__construct($message, $code, $previous); $this->tags = $tags; } /** * @return string[] * @since 9.0.0 */ public function getMissingTags() { return $this->tags; } } public/SystemTag/ISystemTagManagerFactory.php 0000604 00000002767 15247130450 0015340 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\SystemTag; use OCP\IServerContainer; /** * Interface ISystemTagManagerFactory * * Factory interface for system tag managers * * @package OCP\SystemTag * @since 9.0.0 */ interface ISystemTagManagerFactory { /** * Constructor for the system tag manager factory * * @param IServerContainer $serverContainer server container * @since 9.0.0 */ public function __construct(IServerContainer $serverContainer); /** * creates and returns an instance of the system tag manager * * @return ISystemTagManager * @since 9.0.0 */ public function getManager(); /** * creates and returns an instance of the system tag object * mapper * * @return ISystemTagObjectMapper * @since 9.0.0 */ public function getObjectMapper(); } public/IUserBackend.php 0000604 00000002372 15247130450 0011033 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * User Interface version 2 * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Interface IUserBackend * * @package OCP * @since 8.0.0 */ interface IUserBackend { /** * Backend name to be shown in user management * @return string the name of the backend to be shown * @since 8.0.0 */ public function getBackendName(); } public/IUser.php 0000604 00000010060 15247130450 0007554 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface IUser * * @package OCP * @since 8.0.0 */ interface IUser { /** * get the user id * * @return string * @since 8.0.0 */ public function getUID(); /** * get the display name for the user, if no specific display name is set it will fallback to the user id * * @return string * @since 8.0.0 */ public function getDisplayName(); /** * set the display name for the user * * @param string $displayName * @return bool * @since 8.0.0 */ public function setDisplayName($displayName); /** * returns the timestamp of the user's last login or 0 if the user did never * login * * @return int * @since 8.0.0 */ public function getLastLogin(); /** * updates the timestamp of the most recent login of this user * @since 8.0.0 */ public function updateLastLoginTimestamp(); /** * Delete the user * * @return bool * @since 8.0.0 */ public function delete(); /** * Set the password of the user * * @param string $password * @param string $recoveryPassword for the encryption app to reset encryption keys * @return bool * @since 8.0.0 */ public function setPassword($password, $recoveryPassword = null); /** * get the users home folder to mount * * @return string * @since 8.0.0 */ public function getHome(); /** * Get the name of the backend class the user is connected with * * @return string * @since 8.0.0 */ public function getBackendClassName(); /** * check if the backend allows the user to change his avatar on Personal page * * @return bool * @since 8.0.0 */ public function canChangeAvatar(); /** * check if the backend supports changing passwords * * @return bool * @since 8.0.0 */ public function canChangePassword(); /** * check if the backend supports changing display names * * @return bool * @since 8.0.0 */ public function canChangeDisplayName(); /** * check if the user is enabled * * @return bool * @since 8.0.0 */ public function isEnabled(); /** * set the enabled status for the user * * @param bool $enabled * @since 8.0.0 */ public function setEnabled($enabled); /** * get the users email address * * @return string|null * @since 9.0.0 */ public function getEMailAddress(); /** * get the avatar image if it exists * * @param int $size * @return IImage|null * @since 9.0.0 */ public function getAvatarImage($size); /** * get the federation cloud id * * @return string * @since 9.0.0 */ public function getCloudId(); /** * set the email address of the user * * @param string|null $mailAddress * @return void * @since 9.0.0 */ public function setEMailAddress($mailAddress); /** * get the users' quota in human readable form. If a specific quota is not * set for the user, the default value is returned. If a default setting * was not set otherwise, it is return as 'none', i.e. quota is not limited. * * @return string * @since 9.0.0 */ public function getQuota(); /** * set the users' quota * * @param string $quota * @return void * @since 9.0.0 */ public function setQuota($quota); } public/Authentication/IApacheBackend.php 0000604 00000003200 15247130450 0014244 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Authentication/IApacheBackend interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Authentication; /** * Interface IApacheBackend * * @package OCP\Authentication * @since 6.0.0 */ interface IApacheBackend { /** * In case the user has been authenticated by a module true is returned. * * @return boolean whether the module reports a user as currently logged in. * @since 6.0.0 */ public function isSessionActive(); /** * Gets the current logout URL * * @return string * @since 12.0.3 */ public function getLogoutUrl(); /** * Return the id of the current user * @return string * @since 6.0.0 */ public function getCurrentUserId(); } public/Authentication/LoginCredentials/IStore.php 0000604 00000002377 15247130450 0016153 0 ustar 00 <?php /** * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Authentication\LoginCredentials; use OCP\Authentication\Exceptions\CredentialsUnavailableException; /** * @since 12 */ interface IStore { /** * Get login credentials of the currently logged in user * * @since 12 * * @throws CredentialsUnavailableException * @return ICredentials the login credentials of the current user */ public function getLoginCredentials(); } public/Authentication/LoginCredentials/ICredentials.php 0000604 00000002607 15247130450 0017310 0 ustar 00 <?php /** * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Authentication\LoginCredentials; use OCP\Authentication\Exceptions\PasswordUnavailableException; /** * @since 12 */ interface ICredentials { /** * Get the user UID * * @since 12 * * @return string */ public function getUID(); /** * Get the login name the users used to login * * @since 12 * * @return string */ public function getLoginName(); /** * Get the password * * @since 12 * * @return string * @throws PasswordUnavailableException */ public function getPassword(); } public/Authentication/TwoFactorAuth/IProvider.php 0000604 00000003603 15247130450 0016146 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Authentication\TwoFactorAuth; use OCP\IUser; use OCP\Template; /** * @since 9.1.0 */ interface IProvider { /** * Get unique identifier of this 2FA provider * * @since 9.1.0 * * @return string */ public function getId(); /** * Get the display name for selecting the 2FA provider * * Example: "Email" * * @since 9.1.0 * * @return string */ public function getDisplayName(); /** * Get the description for selecting the 2FA provider * * Example: "Get a token via e-mail" * * @since 9.1.0 * * @return string */ public function getDescription(); /** * Get the template for rending the 2FA provider view * * @since 9.1.0 * * @param IUser $user * @return Template */ public function getTemplate(IUser $user); /** * Verify the given challenge * * @since 9.1.0 * * @param IUser $user * @param string $challenge */ public function verifyChallenge(IUser $user, $challenge); /** * Decides whether 2FA is enabled for the given user * * @since 9.1.0 * * @param IUser $user * @return boolean */ public function isTwoFactorAuthEnabledForUser(IUser $user); } public/Authentication/TwoFactorAuth/TwoFactorException.php 0000604 00000002074 15247130450 0020033 0 ustar 00 <?php /** * @author Cornelius Kölbel <cornelius.koelbel@netknights.it> * @copyright Copyright (c) 2016, ownCloud GmbH. * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Authentication\TwoFactorAuth; use Exception; /** * Two Factor Authentication failed * * It defines an Exception a 2FA app can * throw in case of an error. The 2FA Controller will catch this exception and * display this error. * * @since 12 */ class TwoFactorException extends Exception { } public/Authentication/Exceptions/CredentialsUnavailableException.php 0000604 00000001770 15247130450 0022115 0 ustar 00 <?php /** * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Authentication\Exceptions; use Exception; /** * @since 12 */ class CredentialsUnavailableException extends Exception { } public/Authentication/Exceptions/PasswordUnavailableException.php 0000604 00000001733 15247130450 0021461 0 ustar 00 <?php /** * @copyright 2017 Morris Jobke <hey@morrisjobke.de> * * @author 2017 Morris Jobke <hey@morrisjobke.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Authentication\Exceptions; use Exception; /** * @since 12 */ class PasswordUnavailableException extends Exception { } public/Share_Backend_File_Dependent.php 0000604 00000002607 15247130450 0014133 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Interface for share backends that share content that is dependent on files. * Extends the Share_Backend interface. * @since 5.0.0 */ interface Share_Backend_File_Dependent extends Share_Backend { /** * Get the file path of the item * @param string $itemSource * @param string $uidOwner User that is the owner of shared item * @return string|false * @since 5.0.0 */ public function getFilePath($itemSource, $uidOwner); } public/AutoloadNotAllowedException.php 0000604 00000002076 15247130450 0014155 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Exception for when a not allowed path is attempted to be autoloaded * @since 8.2.0 */ class AutoloadNotAllowedException extends \DomainException { /** * @param string $path * @since 8.2.0 */ public function __construct($path) { parent::__construct('Autoload path not allowed: '.$path); } } public/PreConditionNotMetException.php 0000604 00000002061 15247130450 0014132 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Exception if the precondition of the config update method isn't met * @since 8.0.0 */ class PreConditionNotMetException extends \Exception {} public/DB.php 0000604 00000012052 15247130450 0007015 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Dan Bartram <daneybartram@gmail.com> * @author Felix Moeller <mail@felixmoeller.de> * @author Frank Karlitschek <frank@karlitschek.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * DB Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides access to the internal database system. Use this class exlusively if you want to access databases * @deprecated 8.1.0 use methods of \OCP\IDBConnection - \OC::$server->getDatabaseConnection() * @since 4.5.0 */ class DB { /** * Prepare a SQL query * @param string $query Query string * @param int $limit Limit of the SQL statement * @param int $offset Offset of the SQL statement * @return \OC_DB_StatementWrapper prepared SQL query * * SQL query via Doctrine prepare(), needs to be execute()'d! * @deprecated 8.1.0 use prepare() of \OCP\IDBConnection - \OC::$server->getDatabaseConnection() * @since 4.5.0 */ static public function prepare( $query, $limit=null, $offset=null ) { return(\OC_DB::prepare($query, $limit, $offset)); } /** * Insert a row if the matching row does not exists. * * @param string $table The table name (will replace *PREFIX* with the actual prefix) * @param array $input data that should be inserted into the table (column name => value) * @param array|null $compare List of values that should be checked for "if not exists" * If this is null or an empty array, all keys of $input will be compared * @return int number of inserted rows * @throws \Doctrine\DBAL\DBALException * @deprecated 8.1.0 use insertIfNotExist() of \OCP\IDBConnection - \OC::$server->getDatabaseConnection() * @since 5.0.0 - parameter $compare was added in 8.1.0 * */ public static function insertIfNotExist($table, $input, array $compare = null) { return \OC::$server->getDatabaseConnection()->insertIfNotExist($table, $input, $compare); } /** * Gets last value of autoincrement * @param string $table The optional table name (will replace *PREFIX*) and add sequence suffix * @return string * * \Doctrine\DBAL\Connection lastInsertID() * * Call this method right after the insert command or other functions may * cause trouble! * @deprecated 8.1.0 use lastInsertId() of \OCP\IDBConnection - \OC::$server->getDatabaseConnection() * @since 4.5.0 */ public static function insertid($table=null) { return \OC::$server->getDatabaseConnection()->lastInsertId($table); } /** * Start a transaction * @deprecated 8.1.0 use beginTransaction() of \OCP\IDBConnection - \OC::$server->getDatabaseConnection() * @since 4.5.0 */ public static function beginTransaction() { \OC::$server->getDatabaseConnection()->beginTransaction(); } /** * Commit the database changes done during a transaction that is in progress * @deprecated 8.1.0 use commit() of \OCP\IDBConnection - \OC::$server->getDatabaseConnection() * @since 4.5.0 */ public static function commit() { \OC::$server->getDatabaseConnection()->commit(); } /** * Rollback the database changes done during a transaction that is in progress * @deprecated 8.1.0 use rollback() of \OCP\IDBConnection - \OC::$server->getDatabaseConnection() * @since 8.0.0 */ public static function rollback() { \OC::$server->getDatabaseConnection()->rollback(); } /** * Check if a result is an error, works with Doctrine * @param mixed $result * @return bool * @deprecated 8.1.0 Doctrine returns false on error (and throws an exception) * @since 4.5.0 */ public static function isError($result) { // Doctrine returns false on error (and throws an exception) return $result === false; } /** * returns the error code and message as a string for logging * works with DoctrineException * @return string * @deprecated 8.1.0 use getError() of \OCP\IDBConnection - \OC::$server->getDatabaseConnection() * @since 6.0.0 */ public static function getErrorMessage() { return \OC::$server->getDatabaseConnection()->getError(); } } public/DB/QueryBuilder/IFunctionBuilder.php 0000604 00000003531 15247130450 0014640 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\DB\QueryBuilder; /** * This class provides a builder for sql some functions * * @since 12.0.0 */ interface IFunctionBuilder { /** * Calculates the MD5 hash of a given input * * @param mixed $input The input to be hashed * * @return IQueryFunction * @since 12.0.0 */ public function md5($input); /** * Combines two input strings * * @param mixed $x The first input string * @param mixed $y The seccond input string * * @return IQueryFunction * @since 12.0.0 */ public function concat($x, $y); /** * Takes a substring from the input string * * @param mixed $input The input string * @param mixed $start The start of the substring, note that counting starts at 1 * @param mixed $length The length of the substring * * @return IQueryFunction * @since 12.0.0 */ public function substring($input, $start, $length = null); /** * Takes the sum of all rows in a column * * @param mixed $field the column to sum * * @return IQueryFunction * @since 12.0.0 */ public function sum($field); } public/DB/QueryBuilder/ILiteral.php 0000604 00000001674 15247130450 0013146 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\DB\QueryBuilder; /** * @since 8.2.0 */ interface ILiteral { /** * @return string * @since 8.2.0 */ public function __toString(); } public/DB/QueryBuilder/IExpressionBuilder.php 0000604 00000026516 15247130450 0015222 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\DB\QueryBuilder; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; /** * This class provides a wrapper around Doctrine's ExpressionBuilder * @since 8.2.0 */ interface IExpressionBuilder { /** * @since 9.0.0 */ const EQ = ExpressionBuilder::EQ; /** * @since 9.0.0 */ const NEQ = ExpressionBuilder::NEQ; /** * @since 9.0.0 */ const LT = ExpressionBuilder::LT; /** * @since 9.0.0 */ const LTE = ExpressionBuilder::LTE; /** * @since 9.0.0 */ const GT = ExpressionBuilder::GT; /** * @since 9.0.0 */ const GTE = ExpressionBuilder::GTE; /** * Creates a conjunction of the given boolean expressions. * * Example: * * [php] * // (u.type = ?) AND (u.role = ?) * $expr->andX('u.type = ?', 'u.role = ?')); * * @param mixed $x Optional clause. Defaults = null, but requires * at least one defined when converting to string. * * @return \OCP\DB\QueryBuilder\ICompositeExpression * @since 8.2.0 */ public function andX($x = null); /** * Creates a disjunction of the given boolean expressions. * * Example: * * [php] * // (u.type = ?) OR (u.role = ?) * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?')); * * @param mixed $x Optional clause. Defaults = null, but requires * at least one defined when converting to string. * * @return \OCP\DB\QueryBuilder\ICompositeExpression * @since 8.2.0 */ public function orX($x = null); /** * Creates a comparison expression. * * @param mixed $x The left expression. * @param string $operator One of the IExpressionBuilder::* constants. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function comparison($x, $operator, $y, $type = null); /** * Creates an equality comparison expression with the given arguments. * * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> = <right expr>. Example: * * [php] * // u.id = ? * $expr->eq('u.id', '?'); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function eq($x, $y, $type = null); /** * Creates a non equality comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> <> <right expr>. Example: * * [php] * // u.id <> 1 * $q->where($q->expr()->neq('u.id', '1')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function neq($x, $y, $type = null); /** * Creates a lower-than comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> < <right expr>. Example: * * [php] * // u.id < ? * $q->where($q->expr()->lt('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function lt($x, $y, $type = null); /** * Creates a lower-than-equal comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> <= <right expr>. Example: * * [php] * // u.id <= ? * $q->where($q->expr()->lte('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function lte($x, $y, $type = null); /** * Creates a greater-than comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> > <right expr>. Example: * * [php] * // u.id > ? * $q->where($q->expr()->gt('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function gt($x, $y, $type = null); /** * Creates a greater-than-equal comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> >= <right expr>. Example: * * [php] * // u.id >= ? * $q->where($q->expr()->gte('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function gte($x, $y, $type = null); /** * Creates an IS NULL expression with the given arguments. * * @param string $x The field in string format to be restricted by IS NULL. * * @return string * @since 8.2.0 */ public function isNull($x); /** * Creates an IS NOT NULL expression with the given arguments. * * @param string $x The field in string format to be restricted by IS NOT NULL. * * @return string * @since 8.2.0 */ public function isNotNull($x); /** * Creates a LIKE() comparison expression with the given arguments. * * @param string $x Field in string format to be inspected by LIKE() comparison. * @param mixed $y Argument to be used in LIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function like($x, $y, $type = null); /** * Creates a NOT LIKE() comparison expression with the given arguments. * * @param string $x Field in string format to be inspected by NOT LIKE() comparison. * @param mixed $y Argument to be used in NOT LIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function notLike($x, $y, $type = null); /** * Creates a ILIKE() comparison expression with the given arguments. * * @param string $x Field in string format to be inspected by ILIKE() comparison. * @param mixed $y Argument to be used in ILIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 9.0.0 */ public function iLike($x, $y, $type = null); /** * Creates a IN () comparison expression with the given arguments. * * @param string $x The field in string format to be inspected by IN() comparison. * @param string|array $y The placeholder or the array of values to be used by IN() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function in($x, $y, $type = null); /** * Creates a NOT IN () comparison expression with the given arguments. * * @param string $x The field in string format to be inspected by NOT IN() comparison. * @param string|array $y The placeholder or the array of values to be used by NOT IN() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 8.2.0 - Parameter $type was added in 9.0.0 */ public function notIn($x, $y, $type = null); /** * Creates a $x = '' statement, because Oracle needs a different check * * @param string $x The field in string format to be inspected by the comparison. * @return string * @since 13.0.0 */ public function emptyString($x); /** * Creates a `$x <> ''` statement, because Oracle needs a different check * * @param string $x The field in string format to be inspected by the comparison. * @return string * @since 13.0.0 */ public function nonEmptyString($x); /** * Creates a bitwise AND comparison * * @param string|ILiteral $x The field or value to check * @param int $y Bitmap that must be set * @return IQueryFunction * @since 12.0.0 */ public function bitwiseAnd($x, $y); /** * Creates a bitwise OR comparison * * @param string|ILiteral $x The field or value to check * @param int $y Bitmap that must be set * @return IQueryFunction * @since 12.0.0 */ public function bitwiseOr($x, $y); /** * Quotes a given input parameter. * * @param mixed $input The parameter to be quoted. * @param mixed|null $type One of the IQueryBuilder::PARAM_* constants * * @return string * @since 8.2.0 */ public function literal($input, $type = null); /** * Returns a IQueryFunction that casts the column to the given type * * @param string $column * @param mixed $type One of IQueryBuilder::PARAM_* * @return string * @since 9.0.0 */ public function castColumn($column, $type); } public/DB/QueryBuilder/IQueryBuilder.php 0000604 00000061066 15247130450 0014167 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\DB\QueryBuilder; use Doctrine\DBAL\Connection; /** * This class provides a wrapper around Doctrine's QueryBuilder * @since 8.2.0 */ interface IQueryBuilder { /** * @since 9.0.0 */ const PARAM_NULL = \PDO::PARAM_NULL; /** * @since 9.0.0 */ const PARAM_BOOL = \PDO::PARAM_BOOL; /** * @since 9.0.0 */ const PARAM_INT = \PDO::PARAM_INT; /** * @since 9.0.0 */ const PARAM_STR = \PDO::PARAM_STR; /** * @since 9.0.0 */ const PARAM_LOB = \PDO::PARAM_LOB; /** * @since 9.0.0 */ const PARAM_DATE = 'datetime'; /** * @since 9.0.0 */ const PARAM_INT_ARRAY = Connection::PARAM_INT_ARRAY; /** * @since 9.0.0 */ const PARAM_STR_ARRAY = Connection::PARAM_STR_ARRAY; /** * Enable/disable automatic prefixing of table names with the oc_ prefix * * @param bool $enabled If set to true table names will be prefixed with the * owncloud database prefix automatically. * @since 8.2.0 */ public function automaticTablePrefix($enabled); /** * Gets an ExpressionBuilder used for object-oriented construction of query expressions. * This producer method is intended for convenient inline usage. Example: * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where($qb->expr()->eq('u.id', 1)); * </code> * * For more complex expression construction, consider storing the expression * builder object in a local variable. * * @return \OCP\DB\QueryBuilder\IExpressionBuilder * @since 8.2.0 */ public function expr(); /** * Gets an FunctionBuilder used for object-oriented construction of query functions. * This producer method is intended for convenient inline usage. Example: * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where($qb->fun()->md5('u.id')); * </code> * * For more complex function construction, consider storing the function * builder object in a local variable. * * @return \OCP\DB\QueryBuilder\IFunctionBuilder * @since 12.0.0 */ public function func(); /** * Gets the type of the currently built query. * * @return integer * @since 8.2.0 */ public function getType(); /** * Gets the associated DBAL Connection for this query builder. * * @return \OCP\IDBConnection * @since 8.2.0 */ public function getConnection(); /** * Gets the state of this query builder instance. * * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. * @since 8.2.0 */ public function getState(); /** * Executes this query using the bound parameters and their types. * * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} * for insert, update and delete statements. * * @return \Doctrine\DBAL\Driver\Statement|int * @since 8.2.0 */ public function execute(); /** * Gets the complete SQL string formed by the current specifications of this QueryBuilder. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('User', 'u') * echo $qb->getSQL(); // SELECT u FROM User u * </code> * * @return string The SQL query string. * @since 8.2.0 */ public function getSQL(); /** * Sets a query parameter for the query being constructed. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.id = :user_id') * ->setParameter(':user_id', 1); * </code> * * @param string|integer $key The parameter position or name. * @param mixed $value The parameter value. * @param string|null $type One of the IQueryBuilder::PARAM_* constants. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function setParameter($key, $value, $type = null); /** * Sets a collection of query parameters for the query being constructed. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.id = :user_id1 OR u.id = :user_id2') * ->setParameters(array( * ':user_id1' => 1, * ':user_id2' => 2 * )); * </code> * * @param array $params The query parameters to set. * @param array $types The query parameters types to set. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function setParameters(array $params, array $types = array()); /** * Gets all defined query parameters for the query being constructed indexed by parameter index or name. * * @return array The currently defined query parameters indexed by parameter index or name. * @since 8.2.0 */ public function getParameters(); /** * Gets a (previously set) query parameter of the query being constructed. * * @param mixed $key The key (index or name) of the bound parameter. * * @return mixed The value of the bound parameter. * @since 8.2.0 */ public function getParameter($key); /** * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. * * @return array The currently defined query parameter types indexed by parameter index or name. * @since 8.2.0 */ public function getParameterTypes(); /** * Gets a (previously set) query parameter type of the query being constructed. * * @param mixed $key The key (index or name) of the bound parameter type. * * @return mixed The value of the bound parameter type. * @since 8.2.0 */ public function getParameterType($key); /** * Sets the position of the first result to retrieve (the "offset"). * * @param integer $firstResult The first result to return. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function setFirstResult($firstResult); /** * Gets the position of the first result the query object was set to retrieve (the "offset"). * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. * * @return integer The position of the first result. * @since 8.2.0 */ public function getFirstResult(); /** * Sets the maximum number of results to retrieve (the "limit"). * * @param integer $maxResults The maximum number of results to retrieve. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function setMaxResults($maxResults); /** * Gets the maximum number of results the query object was set to retrieve (the "limit"). * Returns NULL if {@link setMaxResults} was not applied to this query builder. * * @return integer The maximum number of results. * @since 8.2.0 */ public function getMaxResults(); /** * Specifies an item that is to be returned in the query result. * Replaces any previously specified selections, if any. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.id', 'p.id') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); * </code> * * @param mixed $select The selection expressions. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function select($select = null); /** * Specifies an item that is to be returned with a different name in the query result. * * <code> * $qb = $conn->getQueryBuilder() * ->selectAlias('u.id', 'user_id') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); * </code> * * @param mixed $select The selection expressions. * @param string $alias The column alias used in the constructed query. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.1 */ public function selectAlias($select, $alias); /** * Specifies an item that is to be returned uniquely in the query result. * * <code> * $qb = $conn->getQueryBuilder() * ->selectDistinct('type') * ->from('users'); * </code> * * @param mixed $select The selection expressions. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 9.0.0 */ public function selectDistinct($select); /** * Adds an item that is to be returned in the query result. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.id') * ->addSelect('p.id') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); * </code> * * @param mixed $select The selection expression. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function addSelect($select = null); /** * Turns the query being built into a bulk delete query that ranges over * a certain table. * * <code> * $qb = $conn->getQueryBuilder() * ->delete('users', 'u') * ->where('u.id = :user_id'); * ->setParameter(':user_id', 1); * </code> * * @param string $delete The table whose rows are subject to the deletion. * @param string $alias The table alias used in the constructed query. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function delete($delete = null, $alias = null); /** * Turns the query being built into a bulk update query that ranges over * a certain table * * <code> * $qb = $conn->getQueryBuilder() * ->update('users', 'u') * ->set('u.password', md5('password')) * ->where('u.id = ?'); * </code> * * @param string $update The table whose rows are subject to the update. * @param string $alias The table alias used in the constructed query. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function update($update = null, $alias = null); /** * Turns the query being built into an insert query that inserts into * a certain table * * <code> * $qb = $conn->getQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?', * 'password' => '?' * ) * ); * </code> * * @param string $insert The table into which the rows should be inserted. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function insert($insert = null); /** * Creates and adds a query root corresponding to the table identified by the * given alias, forming a cartesian product with any existing query roots. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.id') * ->from('users', 'u') * </code> * * @param string $from The table. * @param string|null $alias The alias of the table. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function from($from, $alias = null); /** * Creates and adds a join to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * </code> * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function join($fromAlias, $join, $alias, $condition = null); /** * Creates and adds a join to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * </code> * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function innerJoin($fromAlias, $join, $alias, $condition = null); /** * Creates and adds a left join to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * </code> * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function leftJoin($fromAlias, $join, $alias, $condition = null); /** * Creates and adds a right join to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * </code> * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function rightJoin($fromAlias, $join, $alias, $condition = null); /** * Sets a new value for a column in a bulk update query. * * <code> * $qb = $conn->getQueryBuilder() * ->update('users', 'u') * ->set('u.password', md5('password')) * ->where('u.id = ?'); * </code> * * @param string $key The column to set. * @param string $value The value, expression, placeholder, etc. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function set($key, $value); /** * Specifies one or more restrictions to the query result. * Replaces any previously specified restrictions, if any. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->where('u.id = ?'); * * // You can optionally programatically build and/or expressions * $qb = $conn->getQueryBuilder(); * * $or = $qb->expr()->orx(); * $or->add($qb->expr()->eq('u.id', 1)); * $or->add($qb->expr()->eq('u.id', 2)); * * $qb->update('users', 'u') * ->set('u.password', md5('password')) * ->where($or); * </code> * * @param mixed $predicates The restriction predicates. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function where($predicates); /** * Adds one or more restrictions to the query results, forming a logical * conjunction with any previously specified restrictions. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.username LIKE ?') * ->andWhere('u.is_active = 1'); * </code> * * @param mixed $where The query restrictions. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * * @see where() * @since 8.2.0 */ public function andWhere($where); /** * Adds one or more restrictions to the query results, forming a logical * disjunction with any previously specified restrictions. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->where('u.id = 1') * ->orWhere('u.id = 2'); * </code> * * @param mixed $where The WHERE statement. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * * @see where() * @since 8.2.0 */ public function orWhere($where); /** * Specifies a grouping over the results of the query. * Replaces any previously specified groupings, if any. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->groupBy('u.id'); * </code> * * @param mixed $groupBy The grouping expression. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function groupBy($groupBy); /** * Adds a grouping expression to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->groupBy('u.lastLogin'); * ->addGroupBy('u.createdAt') * </code> * * @param mixed $groupBy The grouping expression. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function addGroupBy($groupBy); /** * Sets a value for a column in an insert query. * * <code> * $qb = $conn->getQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?' * ) * ) * ->setValue('password', '?'); * </code> * * @param string $column The column into which the value should be inserted. * @param string $value The value that should be inserted into the column. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function setValue($column, $value); /** * Specifies values for an insert query indexed by column names. * Replaces any previous values, if any. * * <code> * $qb = $conn->getQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?', * 'password' => '?' * ) * ); * </code> * * @param array $values The values to specify for the insert query indexed by column names. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function values(array $values); /** * Specifies a restriction over the groups of the query. * Replaces any previous having restrictions, if any. * * @param mixed $having The restriction over the groups. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function having($having); /** * Adds a restriction over the groups of the query, forming a logical * conjunction with any existing having restrictions. * * @param mixed $having The restriction to append. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function andHaving($having); /** * Adds a restriction over the groups of the query, forming a logical * disjunction with any existing having restrictions. * * @param mixed $having The restriction to add. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function orHaving($having); /** * Specifies an ordering for the query results. * Replaces any previously specified orderings, if any. * * @param string $sort The ordering expression. * @param string $order The ordering direction. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function orderBy($sort, $order = null); /** * Adds an ordering to the query results. * * @param string $sort The ordering expression. * @param string $order The ordering direction. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function addOrderBy($sort, $order = null); /** * Gets a query part by its name. * * @param string $queryPartName * * @return mixed * @since 8.2.0 */ public function getQueryPart($queryPartName); /** * Gets all query parts. * * @return array * @since 8.2.0 */ public function getQueryParts(); /** * Resets SQL parts. * * @param array|null $queryPartNames * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function resetQueryParts($queryPartNames = null); /** * Resets a single SQL part. * * @param string $queryPartName * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * @since 8.2.0 */ public function resetQueryPart($queryPartName); /** * Creates a new named parameter and bind the value $value to it. * * This method provides a shortcut for PDOStatement::bindValue * when using prepared statements. * * The parameter $value specifies the value that you want to bind. If * $placeholder is not provided bindValue() will automatically create a * placeholder for you. An automatic placeholder will be of the name * ':dcValue1', ':dcValue2' etc. * * For more information see {@link http://php.net/pdostatement-bindparam} * * Example: * <code> * $value = 2; * $q->eq( 'id', $q->bindValue( $value ) ); * $stmt = $q->executeQuery(); // executed with 'id = 2' * </code> * * @license New BSD License * @link http://www.zetacomponents.org * * @param mixed $value * @param mixed $type * @param string $placeHolder The name to bind with. The string must start with a colon ':'. * * @return IParameter * @since 8.2.0 */ public function createNamedParameter($value, $type = self::PARAM_STR, $placeHolder = null); /** * Creates a new positional parameter and bind the given value to it. * * Attention: If you are using positional parameters with the query builder you have * to be very careful to bind all parameters in the order they appear in the SQL * statement , otherwise they get bound in the wrong order which can lead to serious * bugs in your code. * * Example: * <code> * $qb = $conn->getQueryBuilder(); * $qb->select('u.*') * ->from('users', 'u') * ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR)) * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR)) * </code> * * @param mixed $value * @param integer $type * * @return IParameter * @since 8.2.0 */ public function createPositionalParameter($value, $type = self::PARAM_STR); /** * Creates a new parameter * * Example: * <code> * $qb = $conn->getQueryBuilder(); * $qb->select('u.*') * ->from('users', 'u') * ->where('u.username = ' . $qb->createParameter('name')) * ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR)) * </code> * * @param string $name * * @return IParameter * @since 8.2.0 */ public function createParameter($name); /** * Creates a new function * * Attention: Column names inside the call have to be quoted before hand * * Example: * <code> * $qb = $conn->getQueryBuilder(); * $qb->select($qb->createFunction('COUNT(*)')) * ->from('users', 'u') * echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u * </code> * <code> * $qb = $conn->getQueryBuilder(); * $qb->select($qb->createFunction('COUNT(`column`)')) * ->from('users', 'u') * echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u * </code> * * @param string $call * * @return IQueryFunction * @since 8.2.0 */ public function createFunction($call); /** * Used to get the id of the last inserted element * @return int * @throws \BadMethodCallException When being called before an insert query has been run. * @since 9.0.0 */ public function getLastInsertId(); /** * Returns the table name quoted and with database prefix as needed by the implementation * * @param string $table * @return string * @since 9.0.0 */ public function getTableName($table); /** * Returns the column name quoted and with table alias prefix as needed by the implementation * * @param string $column * @param string $tableAlias * @return string * @since 9.0.0 */ public function getColumnName($column, $tableAlias = ''); } public/DB/QueryBuilder/ICompositeExpression.php 0000604 00000003120 15247130450 0015560 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\DB\QueryBuilder; /** * This class provides a wrapper around Doctrine's CompositeExpression * @since 8.2.0 */ interface ICompositeExpression { /** * Adds multiple parts to composite expression. * * @param array $parts * * @return ICompositeExpression * @since 8.2.0 */ public function addMultiple(array $parts = array()); /** * Adds an expression to composite expression. * * @param mixed $part * * @return ICompositeExpression * @since 8.2.0 */ public function add($part); /** * Retrieves the amount of expressions on composite expression. * * @return integer * @since 8.2.0 */ public function count(); /** * Returns the type of this composite expression (AND/OR). * * @return string * @since 8.2.0 */ public function getType(); } public/DB/QueryBuilder/IParameter.php 0000604 00000001676 15247130450 0013474 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\DB\QueryBuilder; /** * @since 8.2.0 */ interface IParameter { /** * @return string * @since 8.2.0 */ public function __toString(); } public/DB/QueryBuilder/IQueryFunction.php 0000604 00000001702 15247130450 0014355 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\DB\QueryBuilder; /** * @since 8.2.0 */ interface IQueryFunction { /** * @return string * @since 8.2.0 */ public function __toString(); } public/Command/ICommand.php 0000604 00000001740 15247130450 0011577 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Command; /** * Interface ICommand * * @package OCP\Command * @since 8.1.0 */ interface ICommand { /** * Run the command * @since 8.1.0 */ public function handle(); } public/Command/IBus.php 0000604 00000002302 15247130450 0010745 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Command; /** * Interface IBus * * @package OCP\Command * @since 8.1.0 */ interface IBus { /** * Schedule a command to be fired * * @param \OCP\Command\ICommand | callable $command * @since 8.1.0 */ public function push($command); /** * Require all commands using a trait to be run synchronous * * @param string $trait * @since 8.1.0 */ public function requireSync($trait); } public/User.php 0000604 00000011637 15247130450 0007456 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lorenzo M. Catucci <lorenzo@sancho.ccd.uniroma2.it> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * User Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides access to the user management. You can get information * about the currently logged in user and the permissions for example * @since 5.0.0 */ class User { /** * Get the user id of the user currently logged in. * @return string uid or false * @deprecated 8.0.0 Use \OC::$server->getUserSession()->getUser()->getUID() * @since 5.0.0 */ public static function getUser() { return \OC_User::getUser(); } /** * Get a list of all users * @param string $search search pattern * @param int|null $limit * @param int|null $offset * @return array an array of all uids * @deprecated 8.1.0 use method search() of \OCP\IUserManager - \OC::$server->getUserManager() * @since 5.0.0 */ public static function getUsers( $search = '', $limit = null, $offset = null ) { return \OC_User::getUsers( $search, $limit, $offset ); } /** * Get the user display name of the user currently logged in. * @param string|null $user user id or null for current user * @return string display name * @deprecated 8.1.0 fetch \OCP\IUser (has getDisplayName()) by using method * get() of \OCP\IUserManager - \OC::$server->getUserManager() * @since 5.0.0 */ public static function getDisplayName( $user = null ) { return \OC_User::getDisplayName( $user ); } /** * Get a list of all display names and user ids. * @param string $search search pattern * @param int|null $limit * @param int|null $offset * @return array an array of all display names (value) and the correspondig uids (key) * @deprecated 8.1.0 use method searchDisplayName() of \OCP\IUserManager - \OC::$server->getUserManager() * @since 5.0.0 */ public static function getDisplayNames( $search = '', $limit = null, $offset = null ) { return \OC_User::getDisplayNames( $search, $limit, $offset ); } /** * Check if the user is logged in * @return boolean * @since 5.0.0 */ public static function isLoggedIn() { return \OC::$server->getUserSession()->isLoggedIn(); } /** * Check if a user exists * @param string $uid the username * @param string $excludingBackend (default none) * @return boolean * @deprecated 8.1.0 use method userExists() of \OCP\IUserManager - \OC::$server->getUserManager() * @since 5.0.0 */ public static function userExists( $uid, $excludingBackend = null ) { return \OC_User::userExists( $uid, $excludingBackend ); } /** * Logs the user out including all the session data * Logout, destroys session * @deprecated 8.0.0 Use \OC::$server->getUserSession()->logout(); * @since 5.0.0 */ public static function logout() { \OC::$server->getUserSession()->logout(); } /** * Check if the password is correct * @param string $uid The username * @param string $password The password * @return string|false username on success, false otherwise * * Check if the password is correct without logging in the user * @deprecated 8.0.0 Use \OC::$server->getUserManager()->checkPassword(); * @since 5.0.0 */ public static function checkPassword( $uid, $password ) { return \OC_User::checkPassword( $uid, $password ); } /** * Check if the user is a admin, redirects to home if not * @since 5.0.0 */ public static function checkAdminUser() { \OC_Util::checkAdminUser(); } /** * Check if the user is logged in, redirects to home if not. With * redirect URL parameter to the request URI. * @since 5.0.0 */ public static function checkLoggedIn() { \OC_Util::checkLoggedIn(); } } public/Session/Exceptions/SessionNotAvailableException.php 0000604 00000001576 15247130450 0020071 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Session\Exceptions; use Exception; /** * @since 9.1.0 */ class SessionNotAvailableException extends Exception { } public/API.php 0000604 00000004460 15247130450 0007145 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Tom Needham <tom@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * API Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides functions to manage apps in ownCloud * @since 5.0.0 * @deprecated 9.1.0 Use the AppFramework */ class API { /** * API authentication levels * @since 8.1.0 */ const GUEST_AUTH = 0; const USER_AUTH = 1; const SUBADMIN_AUTH = 2; const ADMIN_AUTH = 3; /** * API Response Codes * @since 8.1.0 */ const RESPOND_UNAUTHORISED = 997; const RESPOND_SERVER_ERROR = 996; const RESPOND_NOT_FOUND = 998; const RESPOND_UNKNOWN_ERROR = 999; /** * registers an api call * @param string $method the http method * @param string $url the url to match * @param callable $action the function to run * @param string $app the id of the app registering the call * @param int $authLevel the level of authentication required for the call (See `self::*_AUTH` constants) * @param array $defaults * @param array $requirements * @since 5.0.0 * @deprecated 9.1.0 Use the AppFramework */ public static function register($method, $url, $action, $app, $authLevel = self::USER_AUTH, $defaults = array(), $requirements = array()){ \OC_API::register($method, $url, $action, $app, $authLevel, $defaults, $requirements); } } public/Route/IRouter.php 0000604 00000005737 15247130450 0011233 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Route; /** * Interface IRouter * * @package OCP\Route * @since 7.0.0 * @deprecated 9.0.0 */ interface IRouter { /** * Get the files to load the routes from * * @return string[] * @since 7.0.0 * @deprecated 9.0.0 */ public function getRoutingFiles(); /** * @return string * @since 7.0.0 * @deprecated 9.0.0 */ public function getCacheKey(); /** * Loads the routes * * @param null|string $app * @since 7.0.0 * @deprecated 9.0.0 */ public function loadRoutes($app = null); /** * Sets the collection to use for adding routes * * @param string $name Name of the collection to use. * @return void * @since 7.0.0 * @deprecated 9.0.0 */ public function useCollection($name); /** * returns the current collection name in use for adding routes * * @return string the collection name * @since 8.0.0 * @deprecated 9.0.0 */ public function getCurrentCollection(); /** * Create a \OCP\Route\IRoute. * * @param string $name Name of the route to create. * @param string $pattern The pattern to match * @param array $defaults An array of default parameter values * @param array $requirements An array of requirements for parameters (regexes) * @return \OCP\Route\IRoute * @since 7.0.0 * @deprecated 9.0.0 */ public function create($name, $pattern, array $defaults = array(), array $requirements = array()); /** * Find the route matching $url. * * @param string $url The url to find * @throws \Exception * @return void * @since 7.0.0 * @deprecated 9.0.0 */ public function match($url); /** * Get the url generator * * @since 7.0.0 * @deprecated 9.0.0 */ public function getGenerator(); /** * Generate url based on $name and $parameters * * @param string $name Name of the route to use. * @param array $parameters Parameters for the route * @param bool $absolute * @return string * @since 7.0.0 * @deprecated 9.0.0 */ public function generate($name, $parameters = array(), $absolute = false); } public/Route/IRoute.php 0000604 00000005433 15247130450 0011042 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Route; /** * Interface IRoute * * @package OCP\Route * @since 7.0.0 */ interface IRoute { /** * Specify PATCH as the method to use with this route * @return \OCP\Route\IRoute * @since 7.0.0 */ public function patch(); /** * Specify the method when this route is to be used * * @param string $method HTTP method (uppercase) * @return \OCP\Route\IRoute * @since 7.0.0 */ public function method($method); /** * The action to execute when this route matches, includes a file like * it is called directly * * @param string $file * @return void * @since 7.0.0 */ public function actionInclude($file); /** * Specify GET as the method to use with this route * @return \OCP\Route\IRoute * @since 7.0.0 */ public function get(); /** * Specify POST as the method to use with this route * @return \OCP\Route\IRoute * @since 7.0.0 */ public function post(); /** * Specify DELETE as the method to use with this route * @return \OCP\Route\IRoute * @since 7.0.0 */ public function delete(); /** * The action to execute when this route matches * * @param string|callable $class the class or a callable * @param string $function the function to use with the class * @return \OCP\Route\IRoute * * This function is called with $class set to a callable or * to the class with $function * @since 7.0.0 */ public function action($class, $function = null); /** * Defaults to use for this route * * @param array $defaults The defaults * @return \OCP\Route\IRoute * @since 7.0.0 */ public function defaults($defaults); /** * Requirements for this route * * @param array $requirements The requirements * @return \OCP\Route\IRoute * @since 7.0.0 */ public function requirements($requirements); /** * Specify PUT as the method to use with this route * @return \OCP\Route\IRoute * @since 7.0.0 */ public function put(); } public/LDAP/ILDAPProviderFactory.php 0000604 00000002630 15247130450 0013145 0 ustar 00 <?php /** * * @copyright Copyright (c) 2016, Roger Szabo (roger.szabo@web.de) * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\LDAP; use OCP\IServerContainer; /** * Interface ILDAPProviderFactory * * This class is responsible for instantiating and returning an ILDAPProvider * instance. * * @package OCP\LDAP * @since 11.0.0 */ interface ILDAPProviderFactory { /** * Constructor for the LDAP provider factory * * @param IServerContainer $serverContainer server container * @since 11.0.0 */ public function __construct(IServerContainer $serverContainer); /** * creates and returns an instance of the ILDAPProvider * * @return ILDAPProvider * @since 11.0.0 */ public function getLDAPProvider(); } public/LDAP/IDeletionFlagSupport.php 0000604 00000002266 15247130450 0013321 0 ustar 00 <?php /** * * @copyright Copyright (c) 2016, Roger Szabo (roger.szabo@web.de) * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\LDAP; /** * Interface IDeletionFlagSupport * * @package OCP\LDAP * @since 11.0.0 */ interface IDeletionFlagSupport { /** * Flag record for deletion. * @param string $uid user id * @since 11.0.0 */ public function flagRecord($uid); /** * Unflag record for deletion. * @param string $uid user id * @since 11.0.0 */ public function unflagRecord($uid); } public/LDAP/ILDAPProvider.php 0000604 00000005235 15247130450 0011621 0 ustar 00 <?php /** * * @copyright Copyright (c) 2016, Roger Szabo (roger.szabo@web.de) * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\LDAP; /** * Interface ILDAPProvider * * @package OCP\LDAP * @since 11.0.0 */ interface ILDAPProvider { /** * Translate a user id to LDAP DN. * @param string $uid user id * @return string * @since 11.0.0 */ public function getUserDN($uid); /** * Translate a LDAP DN to an internal user name. * @param string $dn LDAP DN * @return string with the internal user name * @throws \Exception if translation was unsuccessful * @since 11.0.0 */ public function getUserName($dn); /** * Convert a stored DN so it can be used as base parameter for LDAP queries. * @param string $dn the DN * @return string * @since 11.0.0 */ public function DNasBaseParameter($dn); /** * Sanitize a DN received from the LDAP server. * @param array $dn the DN in question * @return array the sanitized DN * @since 11.0.0 */ public function sanitizeDN($dn); /** * Return a new LDAP connection resource for the specified user. * @param string $uid user id * @return resource of the LDAP connection * @since 11.0.0 */ public function getLDAPConnection($uid); /** * Get the LDAP base for users. * @param string $uid user id * @return string the base for users * @throws \Exception if user id was not found in LDAP * @since 11.0.0 */ public function getLDAPBaseUsers($uid); /** * Get the LDAP base for groups. * @param string $uid user id * @return string the base for groups * @throws \Exception if user id was not found in LDAP * @since 11.0.0 */ public function getLDAPBaseGroups($uid); /** * Check whether a LDAP DN exists * @param string $dn LDAP DN * @return bool whether the DN exists * @since 11.0.0 */ public function dnExists($dn); /** * Clear the cache if a cache is used, otherwise do nothing. * @param string $uid user id * @since 11.0.0 */ public function clearCache($uid); } public/IRequest.php 0000604 00000016427 15247130450 0010303 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Request interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This interface provides an immutable object with with accessors to * request variables and headers. * * Access request variables by method and name. * * Examples: * * $request->post['myvar']; // Only look for POST variables * $request->myvar; or $request->{'myvar'}; or $request->{$myvar} * Looks in the combined GET, POST and urlParams array. * * If you access e.g. ->post but the current HTTP request method * is GET a \LogicException will be thrown. * * NOTE: * - When accessing ->put a stream resource is returned and the accessor * will return false on subsequent access to ->put or ->patch. * - When accessing ->patch and the Content-Type is either application/json * or application/x-www-form-urlencoded (most cases) it will act like ->get * and ->post and return an array. Otherwise the raw data will be returned. * * @property-read string[] $server * @property-read string[] $urlParams * @since 6.0.0 */ interface IRequest { /** * @since 9.1.0 */ const USER_AGENT_CLIENT_ANDROID = '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/'; /** * @since 9.1.0 */ const USER_AGENT_CLIENT_DESKTOP = '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/'; /** * @since 9.1.0 */ const USER_AGENT_CLIENT_IOS = '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/'; /** * @param string $name * * @return string * @since 6.0.0 */ public function getHeader($name); /** * Lets you access post and get parameters by the index * In case of json requests the encoded json body is accessed * * @param string $key the key which you want to access in the URL Parameter * placeholder, $_POST or $_GET array. * The priority how they're returned is the following: * 1. URL parameters * 2. POST parameters * 3. GET parameters * @param mixed $default If the key is not found, this value will be returned * @return mixed the content of the array * @since 6.0.0 */ public function getParam($key, $default = null); /** * Returns all params that were received, be it from the request * * (as GET or POST) or through the URL by the route * * @return array the array with all parameters * @since 6.0.0 */ public function getParams(); /** * Returns the method of the request * * @return string the method of the request (POST, GET, etc) * @since 6.0.0 */ public function getMethod(); /** * Shortcut for accessing an uploaded file through the $_FILES array * * @param string $key the key that will be taken from the $_FILES array * @return array the file in the $_FILES element * @since 6.0.0 */ public function getUploadedFile($key); /** * Shortcut for getting env variables * * @param string $key the key that will be taken from the $_ENV array * @return array the value in the $_ENV element * @since 6.0.0 */ public function getEnv($key); /** * Shortcut for getting cookie variables * * @param string $key the key that will be taken from the $_COOKIE array * @return string|null the value in the $_COOKIE element * @since 6.0.0 */ public function getCookie($key); /** * Checks if the CSRF check was correct * * @return bool true if CSRF check passed * @since 6.0.0 */ public function passesCSRFCheck(); /** * Checks if the strict cookie has been sent with the request if the request * is including any cookies. * * @return bool * @since 9.0.0 */ public function passesStrictCookieCheck(); /** * Checks if the lax cookie has been sent with the request if the request * is including any cookies. * * @return bool * @since 9.0.0 */ public function passesLaxCookieCheck(); /** * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging * If `mod_unique_id` is installed this value will be taken. * * @return string * @since 8.1.0 */ public function getId(); /** * Returns the remote address, if the connection came from a trusted proxy * and `forwarded_for_headers` has been configured then the IP address * specified in this header will be returned instead. * Do always use this instead of $_SERVER['REMOTE_ADDR'] * * @return string IP address * @since 8.1.0 */ public function getRemoteAddress(); /** * Returns the server protocol. It respects reverse proxy servers and load * balancers. * * @return string Server protocol (http or https) * @since 8.1.0 */ public function getServerProtocol(); /** * Returns the used HTTP protocol. * * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0. * @since 8.2.0 */ public function getHttpProtocol(); /** * Returns the request uri, even if the website uses one or more * reverse proxies * * @return string * @since 8.1.0 */ public function getRequestUri(); /** * Get raw PathInfo from request (not urldecoded) * * @throws \Exception * @return string Path info * @since 8.1.0 */ public function getRawPathInfo(); /** * Get PathInfo from request * * @throws \Exception * @return string|false Path info or false when not found * @since 8.1.0 */ public function getPathInfo(); /** * Returns the script name, even if the website uses one or more * reverse proxies * * @return string the script name * @since 8.1.0 */ public function getScriptName(); /** * Checks whether the user agent matches a given regex * * @param array $agent array of agent names * @return bool true if at least one of the given agent matches, false otherwise * @since 8.1.0 */ public function isUserAgent(array $agent); /** * Returns the unverified server host from the headers without checking * whether it is a trusted domain * * @return string Server host * @since 8.1.0 */ public function getInsecureServerHost(); /** * Returns the server host from the headers, or the first configured * trusted domain if the host isn't in the trusted list * * @return string Server host * @since 8.1.0 */ public function getServerHost(); } public/IAvatar.php 0000604 00000004363 15247130450 0010065 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; use OCP\Files\File; use OCP\Files\NotFoundException; /** * This class provides avatar functionality * @since 6.0.0 */ interface IAvatar { /** * get the users avatar * @param int $size size in px of the avatar, avatars are square, defaults to 64, -1 can be used to not scale the image * @return boolean|\OCP\IImage containing the avatar or false if there's no image * @since 6.0.0 - size of -1 was added in 9.0.0 */ public function get($size = 64); /** * Check if an avatar exists for the user * * @return bool * @since 8.1.0 */ public function exists(); /** * sets the users avatar * @param \OCP\IImage|resource|string $data An image object, imagedata or path to set a new avatar * @throws \Exception if the provided file is not a jpg or png image * @throws \Exception if the provided image is not valid * @throws \OC\NotSquareException if the image is not square * @return void * @since 6.0.0 */ public function set($data); /** * remove the users avatar * @return void * @since 6.0.0 */ public function remove(); /** * Get the file of the avatar * @param int $size -1 can be used to not scale the image * @return File * @throws NotFoundException * @since 9.0.0 */ public function getFile($size); } public/Files/Notify/INotifyHandler.php 0000604 00000003512 15247130450 0013722 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Notify; /** * Provides access to detected changes in the storage by either actively listening * or getting the list of changes that happened in the background * * @since 12.0.0 */ interface INotifyHandler { /** * Start listening for update notifications * * The provided callback will be called for every incoming notification with the following parameters * - IChange|IRenameChange $change * * Note that this call is blocking and will not exit on it's own, to stop listening for notifications return `false` from the callback * * @param callable $callback * * @since 12.0.0 */ public function listen(callable $callback); /** * Get all changes detected since the start of the notify process or the last call to getChanges * * @return IChange[] * * @since 12.0.0 */ public function getChanges(); /** * Stop listening for changes * * Note that any pending changes will be discarded * * @since 12.0.0 */ public function stop(); } public/Files/Notify/IChange.php 0000604 00000002714 15247130450 0012344 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Notify; /** * Represents a detected change in the storage * * @since 12.0.0 */ interface IChange { const ADDED = 1; const REMOVED = 2; const MODIFIED = 3; const RENAMED = 4; /** * Get the type of the change * * @return int IChange::ADDED, IChange::REMOVED, IChange::MODIFIED or IChange::RENAMED * * @since 12.0.0 */ public function getType(); /** * Get the path of the file that was changed relative to the root of the storage * * Note, for rename changes this path is the old path for the file * * @return mixed * * @since 12.0.0 */ public function getPath(); } public/Files/Notify/IRenameChange.php 0000604 00000002205 15247130450 0013467 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Notify; /** * Represents a detected rename change * * @since 12.0.0 */ interface IRenameChange extends IChange { /** * Get the new path of the renamed file relative to the storage root * * @return string * * @since 12.0.0 */ public function getTargetPath(); } public/Files/Storage/ILockingStorage.php 0000604 00000004332 15247130450 0014224 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Storage; use OCP\Lock\ILockingProvider; /** * Storage backends that require explicit locking * * Storage backends implementing this interface do not need to implement their own locking implementation but should use the provided lockingprovider instead * The implementation of the locking methods only need to map internal storage paths to "lock keys" * * @since 9.0.0 */ interface ILockingStorage { /** * @param string $path The path of the file to acquire the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException * @since 9.0.0 */ public function acquireLock($path, $type, ILockingProvider $provider); /** * @param string $path The path of the file to acquire the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException * @since 9.0.0 */ public function releaseLock($path, $type, ILockingProvider $provider); /** * @param string $path The path of the file to change the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException * @since 9.0.0 */ public function changeLock($path, $type, ILockingProvider $provider); } public/Files/Storage/INotifyStorage.php 0000604 00000003762 15247130450 0014114 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> * * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Storage; use OCP\Files\Notify\INotifyHandler; /** * Storage backend that support active notifications * * @since 9.1.0 */ interface INotifyStorage { const NOTIFY_ADDED = 1; const NOTIFY_REMOVED = 2; const NOTIFY_MODIFIED = 3; const NOTIFY_RENAMED = 4; /** * Start listening for update notifications * * The provided callback will be called for every incoming notification with the following parameters * - int $type the type of update, one of the INotifyStorage::NOTIFY_* constants * - string $path the path of the update * - string $renameTarget the target of the rename operation, only provided for rename updates * * Note that this call is blocking and will not exit on it's own, to stop listening for notifications return `false` from the callback * * @param string $path * @param callable $callback * * @since 9.1.0 * @deprecated 12.0.0 use INotifyStorage::notify()->listen() instead */ public function listen($path, callable $callback); /** * Start the notification handler for this storage * * @param $path * @return INotifyHandler * * @since 12.0.0 */ public function notify($path); } public/Files/Storage/IStorage.php 0000604 00000024017 15247130450 0012717 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/Storage interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files\Storage; use OCP\Files\Cache\ICache; use OCP\Files\Cache\IPropagator; use OCP\Files\Cache\IScanner; use OCP\Files\Cache\IUpdater; use OCP\Files\Cache\IWatcher; use OCP\Files\InvalidPathException; /** * Provide a common interface to all different storage options * * All paths passed to the storage are relative to the storage and should NOT have a leading slash. * * @since 9.0.0 */ interface IStorage { /** * $parameters is a free form array with the configuration options needed to construct the storage * * @param array $parameters * @since 9.0.0 */ public function __construct($parameters); /** * Get the identifier for the storage, * the returned id should be the same for every storage object that is created with the same parameters * and two storage objects with the same id should refer to two storages that display the same files. * * @return string * @since 9.0.0 */ public function getId(); /** * see http://php.net/manual/en/function.mkdir.php * implementations need to implement a recursive mkdir * * @param string $path * @return bool * @since 9.0.0 */ public function mkdir($path); /** * see http://php.net/manual/en/function.rmdir.php * * @param string $path * @return bool * @since 9.0.0 */ public function rmdir($path); /** * see http://php.net/manual/en/function.opendir.php * * @param string $path * @return resource|false * @since 9.0.0 */ public function opendir($path); /** * see http://php.net/manual/en/function.is-dir.php * * @param string $path * @return bool * @since 9.0.0 */ public function is_dir($path); /** * see http://php.net/manual/en/function.is-file.php * * @param string $path * @return bool * @since 9.0.0 */ public function is_file($path); /** * see http://php.net/manual/en/function.stat.php * only the following keys are required in the result: size and mtime * * @param string $path * @return array|false * @since 9.0.0 */ public function stat($path); /** * see http://php.net/manual/en/function.filetype.php * * @param string $path * @return string|false * @since 9.0.0 */ public function filetype($path); /** * see http://php.net/manual/en/function.filesize.php * The result for filesize when called on a folder is required to be 0 * * @param string $path * @return int|false * @since 9.0.0 */ public function filesize($path); /** * check if a file can be created in $path * * @param string $path * @return bool * @since 9.0.0 */ public function isCreatable($path); /** * check if a file can be read * * @param string $path * @return bool * @since 9.0.0 */ public function isReadable($path); /** * check if a file can be written to * * @param string $path * @return bool * @since 9.0.0 */ public function isUpdatable($path); /** * check if a file can be deleted * * @param string $path * @return bool * @since 9.0.0 */ public function isDeletable($path); /** * check if a file can be shared * * @param string $path * @return bool * @since 9.0.0 */ public function isSharable($path); /** * get the full permissions of a path. * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php * * @param string $path * @return int * @since 9.0.0 */ public function getPermissions($path); /** * see http://php.net/manual/en/function.file_exists.php * * @param string $path * @return bool * @since 9.0.0 */ public function file_exists($path); /** * see http://php.net/manual/en/function.filemtime.php * * @param string $path * @return int|false * @since 9.0.0 */ public function filemtime($path); /** * see http://php.net/manual/en/function.file_get_contents.php * * @param string $path * @return string|false * @since 9.0.0 */ public function file_get_contents($path); /** * see http://php.net/manual/en/function.file_put_contents.php * * @param string $path * @param string $data * @return bool * @since 9.0.0 */ public function file_put_contents($path, $data); /** * see http://php.net/manual/en/function.unlink.php * * @param string $path * @return bool * @since 9.0.0 */ public function unlink($path); /** * see http://php.net/manual/en/function.rename.php * * @param string $path1 * @param string $path2 * @return bool * @since 9.0.0 */ public function rename($path1, $path2); /** * see http://php.net/manual/en/function.copy.php * * @param string $path1 * @param string $path2 * @return bool * @since 9.0.0 */ public function copy($path1, $path2); /** * see http://php.net/manual/en/function.fopen.php * * @param string $path * @param string $mode * @return resource|false * @since 9.0.0 */ public function fopen($path, $mode); /** * get the mimetype for a file or folder * The mimetype for a folder is required to be "httpd/unix-directory" * * @param string $path * @return string|false * @since 9.0.0 */ public function getMimeType($path); /** * see http://php.net/manual/en/function.hash-file.php * * @param string $type * @param string $path * @param bool $raw * @return string|false * @since 9.0.0 */ public function hash($type, $path, $raw = false); /** * see http://php.net/manual/en/function.free_space.php * * @param string $path * @return int|false * @since 9.0.0 */ public function free_space($path); /** * see http://php.net/manual/en/function.touch.php * If the backend does not support the operation, false should be returned * * @param string $path * @param int $mtime * @return bool * @since 9.0.0 */ public function touch($path, $mtime = null); /** * get the path to a local version of the file. * The local version of the file can be temporary and doesn't have to be persistent across requests * * @param string $path * @return string|false * @since 9.0.0 */ public function getLocalFile($path); /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @return bool * @since 9.0.0 * * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. * returning true for other changes in the folder is optional */ public function hasUpdated($path, $time); /** * get the ETag for a file or folder * * @param string $path * @return string|false * @since 9.0.0 */ public function getETag($path); /** * Returns whether the storage is local, which means that files * are stored on the local filesystem instead of remotely. * Calling getLocalFile() for local storages should always * return the local files, whereas for non-local storages * it might return a temporary file. * * @return bool true if the files are stored locally, false otherwise * @since 9.0.0 */ public function isLocal(); /** * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class * * @param string $class * @return bool * @since 9.0.0 */ public function instanceOfStorage($class); /** * A custom storage implementation can return an url for direct download of a give file. * * For now the returned array can hold the parameter url - in future more attributes might follow. * * @param string $path * @return array|false * @since 9.0.0 */ public function getDirectDownload($path); /** * @param string $path the path of the target folder * @param string $fileName the name of the file itself * @return void * @throws InvalidPathException * @since 9.0.0 */ public function verifyPath($path, $fileName); /** * @param \OCP\Files\Storage|\OCP\Files\Storage\IStorage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool * @since 9.0.0 */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath); /** * @param \OCP\Files\Storage|\OCP\Files\Storage\IStorage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool * @since 9.0.0 */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath); /** * Test a storage for availability * * @since 9.0.0 * @return bool */ public function test(); /** * @since 9.0.0 * @return array [ available, last_checked ] */ public function getAvailability(); /** * @since 9.0.0 * @param bool $isAvailable */ public function setAvailability($isAvailable); /** * @param string $path path for which to retrieve the owner * @since 9.0.0 */ public function getOwner($path); /** * @return ICache * @since 9.0.0 */ public function getCache(); /** * @return IPropagator * @since 9.0.0 */ public function getPropagator(); /** * @return IScanner * @since 9.0.0 */ public function getScanner(); /** * @return IUpdater * @since 9.0.0 */ public function getUpdater(); /** * @return IWatcher * @since 9.0.0 */ public function getWatcher(); } public/Files/Storage/IStorageFactory.php 0000604 00000003256 15247130450 0014251 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Storage; use OCP\Files\Mount\IMountPoint; /** * Creates storage instances and manages and applies storage wrappers * @since 8.0.0 */ interface IStorageFactory { /** * allow modifier storage behaviour by adding wrappers around storages * * $callback should be a function of type (string $mountPoint, Storage $storage) => Storage * * @param string $wrapperName * @param callable $callback * @return bool true if the wrapper was added, false if there was already a wrapper with this * name registered * @since 8.0.0 */ public function addStorageWrapper($wrapperName, $callback); /** * @param \OCP\Files\Mount\IMountPoint $mountPoint * @param string $class * @param array $arguments * @return \OCP\Files\Storage * @since 8.0.0 */ public function getInstance(IMountPoint $mountPoint, $class, $arguments); } public/Files/StorageBadConfigException.php 0000604 00000002442 15247130450 0014614 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jesús Macias <jmacias@solidgear.es> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files; /** * Storage has bad or missing config params * @since 9.0.0 */ class StorageBadConfigException extends StorageNotAvailableException { /** * ExtStorageBadConfigException constructor. * * @param string $message * @param int $code * @param \Exception $previous * @since 9.0.0 */ public function __construct($message = '', \Exception $previous = null) { $l = \OC::$server->getL10N('core'); parent::__construct($l->t('Storage incomplete configuration. %s', $message), self::STATUS_INCOMPLETE_CONF, $previous); } } public/Files/FileNameTooLongException.php 0000604 00000002305 15247130450 0014433 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/ReservedWordException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Class FileNameTooLongException * * @package OCP\Files * @since 8.1.0 */ class FileNameTooLongException extends InvalidPathException { } public/Files/StorageTimeoutException.php 0000604 00000002404 15247130450 0014424 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jesús Macias <jmacias@solidgear.es> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files; /** * Storage authentication exception * @since 9.0.0 */ class StorageTimeoutException extends StorageNotAvailableException { /** * StorageTimeoutException constructor. * * @param string $message * @param int $code * @param \Exception $previous * @since 9.0.0 */ public function __construct($message = '', \Exception $previous = null) { $l = \OC::$server->getL10N('core'); parent::__construct($l->t('Storage connection timeout. %s', $message), self::STATUS_TIMEOUT, $previous); } } public/Files/StorageAuthException.php 0000604 00000002375 15247130450 0013706 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jesús Macias <jmacias@solidgear.es> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files; /** * Storage authentication exception * @since 9.0.0 */ class StorageAuthException extends StorageNotAvailableException { /** * StorageAuthException constructor. * * @param string $message * @param int $code * @param \Exception $previous * @since 9.0.0 */ public function __construct($message = '', \Exception $previous = null) { $l = \OC::$server->getL10N('core'); parent::__construct($l->t('Storage unauthorized. %s', $message), self::STATUS_UNAUTHORIZED, $previous); } } public/Files/IMimeTypeDetector.php 0000604 00000003701 15247130450 0013127 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Interface IMimeTypeDetector * @package OCP\Files * @since 8.2.0 * * Interface to handle mimetypes (detection and icon retrieval) **/ interface IMimeTypeDetector { /** * detect mimetype only based on filename, content of file is not used * @param string $path * @return string * @since 8.2.0 **/ public function detectPath($path); /** * detect mimetype based on both filename and content * * @param string $path * @return string * @since 8.2.0 */ public function detect($path); /** * Get a secure mimetype that won't expose potential XSS. * * @param string $mimeType * @return string * @since 8.2.0 */ public function getSecureMimeType($mimeType); /** * detect mimetype based on the content of a string * * @param string $data * @return string * @since 8.2.0 */ public function detectString($data); /** * Get path to the icon of a file type * @param string $mimeType the MIME type * @return string the url * @since 8.2.0 */ public function mimeTypeIcon($mimeType); } public/Files/StorageConnectionException.php 0000604 00000002416 15247130450 0015100 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jesús Macias <jmacias@solidgear.es> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files; /** * Storage authentication exception * @since 9.0.0 */ class StorageConnectionException extends StorageNotAvailableException { /** * StorageConnectionException constructor. * * @param string $message * @param int $code * @param \Exception $previous * @since 9.0.0 */ public function __construct($message = '', \Exception $previous = null) { $l = \OC::$server->getL10N('core'); parent::__construct($l->t('Storage connection error. %s', $message), self::STATUS_NETWORK_ERROR, $previous); } } public/Files/Config/IMountProvider.php 0000604 00000002310 15247130450 0013721 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Config; use OCP\Files\Storage\IStorageFactory; use OCP\IUser; /** * Provides * @since 8.0.0 */ interface IMountProvider { /** * Get all mountpoints applicable for the user * * @param \OCP\IUser $user * @param \OCP\Files\Storage\IStorageFactory $loader * @return \OCP\Files\Mount\IMountPoint[] * @since 8.0.0 */ public function getMountsForUser(IUser $user, IStorageFactory $loader); } public/Files/Config/ICachedMountInfo.php 0000604 00000003355 15247130450 0014124 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Config; use OCP\Files\Node; use OCP\IUser; /** * Holds information about a mount for a user * * @since 9.0.0 */ interface ICachedMountInfo { /** * @return IUser * @since 9.0.0 */ public function getUser(); /** * @return int the numeric storage id of the mount * @since 9.0.0 */ public function getStorageId(); /** * @return int the fileid of the root of the mount * @since 9.0.0 */ public function getRootId(); /** * @return Node the root node of the mount * @since 9.0.0 */ public function getMountPointNode(); /** * @return string the mount point of the mount for the user * @since 9.0.0 */ public function getMountPoint(); /** * Get the id of the configured mount * * @return int|null mount id or null if not applicable * @since 9.1.0 */ public function getMountId(); /** * Get the internal path (within the storage) of the root of the mount * * @return string * @since 11.0.0 */ public function getRootInternalPath(); } public/Files/Config/IMountProviderCollection.php 0000604 00000003600 15247130450 0015740 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Config; use OCP\IUser; /** * Manages the different mount providers * @since 8.0.0 */ interface IMountProviderCollection { /** * Get all configured mount points for the user * * @param \OCP\IUser $user * @return \OCP\Files\Mount\IMountPoint[] * @since 8.0.0 */ public function getMountsForUser(IUser $user); /** * Get the configured home mount for this user * * @param \OCP\IUser $user * @return \OCP\Files\Mount\IMountPoint * @since 9.1.0 */ public function getHomeMountForUser(IUser $user); /** * Add a provider for mount points * * @param \OCP\Files\Config\IMountProvider $provider * @since 8.0.0 */ public function registerProvider(IMountProvider $provider); /** * Add a provider for home mount points * * @param \OCP\Files\Config\IHomeMountProvider $provider * @since 9.1.0 */ public function registerHomeProvider(IHomeMountProvider $provider); /** * Get the mount cache which can be used to search for mounts without setting up the filesystem * * @return IUserMountCache * @since 9.0.0 */ public function getMountCache(); } public/Files/Config/IHomeMountProvider.php 0000604 00000002250 15247130450 0014535 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Config; use OCP\Files\Storage\IStorageFactory; use OCP\IUser; /** * Provides * * @since 9.1.0 */ interface IHomeMountProvider { /** * Get all mountpoints applicable for the user * * @param \OCP\IUser $user * @param \OCP\Files\Storage\IStorageFactory $loader * @return \OCP\Files\Mount\IMountPoint|null * @since 9.1.0 */ public function getHomeMountForUser(IUser $user, IStorageFactory $loader); } public/Files/Config/IUserMountCache.php 0000604 00000005025 15247130450 0013777 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Config; use OCP\Files\Mount\IMountPoint; use OCP\IUser; /** * Cache mounts points per user in the cache so we can easily look them up * * @since 9.0.0 */ interface IUserMountCache { /** * Register mounts for a user to the cache * * @param IUser $user * @param IMountPoint[] $mounts * @since 9.0.0 */ public function registerMounts(IUser $user, array $mounts); /** * Get all cached mounts for a user * * @param IUser $user * @return ICachedMountInfo[] * @since 9.0.0 */ public function getMountsForUser(IUser $user); /** * Get all cached mounts by storage * * @param int $numericStorageId * @param string|null $user limit the results to a single user @since 12.0.0 * @return ICachedMountInfo[] * @since 9.0.0 */ public function getMountsForStorageId($numericStorageId, $user = null); /** * Get all cached mounts by root * * @param int $rootFileId * @return ICachedMountInfo[] * @since 9.0.0 */ public function getMountsForRootId($rootFileId); /** * Get all cached mounts that contain a file * * @param int $fileId * @param string|null $user optionally restrict the results to a single user @since 12.0.0 * @return ICachedMountInfo[] * @since 9.0.0 */ public function getMountsForFileId($fileId, $user = null); /** * Remove all cached mounts for a user * * @param IUser $user * @since 9.0.0 */ public function removeUserMounts(IUser $user); /** * Remove all mounts for a user and storage * * @param $storageId * @param string $userId * @return mixed * @since 9.0.0 */ public function removeUserStorageMount($storageId, $userId); /** * Remove all cached mounts for a storage * * @param $storageId * @return mixed * @since 9.0.0 */ public function remoteStorageMounts($storageId); } public/Files/IRootFolder.php 0000604 00000002206 15247130450 0011762 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files; use OC\Hooks\Emitter; /** * Interface IRootFolder * * @package OCP\Files * @since 8.0.0 */ interface IRootFolder extends Folder, Emitter { /** * Returns a view to user's files folder * * @param String $userId user ID * @return \OCP\Files\Folder * @since 8.2.0 */ public function getUserFolder($userId); } public/Files/LockNotAcquiredException.php 0000604 00000003534 15247130450 0014505 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Owen Winkler <a_github@midnightcircus.com> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/LockNotAcquiredException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for a file that is locked * @since 7.0.0 */ class LockNotAcquiredException extends \Exception { /** @var string $path The path that could not be locked */ public $path; /** @var integer $lockType The type of the lock that was attempted */ public $lockType; /** * @since 7.0.0 */ public function __construct($path, $lockType, $code = 0, \Exception $previous = null) { $message = \OC::$server->getL10N('core')->t('Could not obtain lock type %d on "%s".', array($lockType, $path)); parent::__construct($message, $code, $previous); } /** * custom string representation of object * * @return string * @since 7.0.0 */ public function __toString() { return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; } } public/Files/NotFoundException.php 0000604 00000002220 15247130450 0013201 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/NotFoundException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for not found entity * @since 6.0.0 */ class NotFoundException extends \Exception {} public/Files/ReservedWordException.php 0000604 00000002246 15247130450 0014070 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/ReservedWordException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for invalid path * @since 8.1.0 */ class ReservedWordException extends InvalidPathException { } public/Files/Search/ISearchBinaryOperator.php 0000604 00000002561 15247130450 0015202 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Search; /** * @since 12.0.0 */ interface ISearchBinaryOperator extends ISearchOperator { const OPERATOR_AND = 'and'; const OPERATOR_OR = 'or'; const OPERATOR_NOT = 'not'; /** * The type of binary operator * * One of the ISearchBinaryOperator::OPERATOR_* constants * * @return string * @since 12.0.0 */ public function getType(); /** * The arguments for the binary operator * * One argument for the 'not' operator and two for 'and' and 'or' * * @return ISearchOperator[] * @since 12.0.0 */ public function getArguments(); } public/Files/Search/ISearchComparison.php 0000604 00000003105 15247130450 0014347 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Search; /** * @since 12.0.0 */ interface ISearchComparison extends ISearchOperator { const COMPARE_EQUAL = 'eq'; const COMPARE_GREATER_THAN = 'gt'; const COMPARE_GREATER_THAN_EQUAL = 'gte'; const COMPARE_LESS_THAN = 'lt'; const COMPARE_LESS_THAN_EQUAL = 'lte'; const COMPARE_LIKE = 'like'; /** * Get the type of comparison, one of the ISearchComparison::COMPARE_* constants * * @return string * @since 12.0.0 */ public function getType(); /** * Get the name of the field to compare with * * i.e. 'size', 'name' or 'mimetype' * * @return string * @since 12.0.0 */ public function getField(); /** * Get the value to compare the field with * * @return string|integer|\DateTime * @since 12.0.0 */ public function getValue(); } public/Files/Search/ISearchOperator.php 0000604 00000001577 15247130450 0014043 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Search; /** * @since 12.0.0 */ interface ISearchOperator { } public/Files/Search/ISearchOrder.php 0000604 00000002361 15247130450 0013313 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Search; /** * @since 12.0.0 */ interface ISearchOrder { const DIRECTION_ASCENDING = 'asc'; const DIRECTION_DESCENDING = 'desc'; /** * The direction to sort in, either ISearchOrder::DIRECTION_ASCENDING or ISearchOrder::DIRECTION_DESCENDING * * @return string * @since 12.0.0 */ public function getDirection(); /** * The field to sort on * * @return string * @since 12.0.0 */ public function getField(); } public/Files/Search/ISearchQuery.php 0000604 00000002735 15247130450 0013352 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\Search; use OCP\IUser; /** * @since 12.0.0 */ interface ISearchQuery { /** * @return ISearchOperator * @since 12.0.0 */ public function getSearchOperation(); /** * Get the maximum number of results to return * * @return integer * @since 12.0.0 */ public function getLimit(); /** * Get the offset for returned results * * @return integer * @since 12.0.0 */ public function getOffset(); /** * The fields and directions to order by * * @return ISearchOrder[] * @since 12.0.0 */ public function getOrder(); /** * The user that issued the search * * @return IUser * @since 12.0.0 */ public function getUser(); } public/Files/EmptyFileNameException.php 0000604 00000001722 15247130450 0014152 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files; /** * Class EmptyFileNameException * * @package OCP\Files * @since 9.2.0 */ class EmptyFileNameException extends InvalidPathException { } public/Files/Mount/IMountManager.php 0000604 00000004277 15247130450 0013414 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Mount; /** * Interface IMountManager * * Manages all mounted storages in the system * @since 8.2.0 */ interface IMountManager { /** * Add a new mount * * @param \OCP\Files\Mount\IMountPoint $mount * @since 8.2.0 */ public function addMount(IMountPoint $mount); /** * Remove a mount * * @param string $mountPoint * @since 8.2.0 */ public function removeMount($mountPoint); /** * Change the location of a mount * * @param string $mountPoint * @param string $target * @since 8.2.0 */ public function moveMount($mountPoint, $target); /** * Find the mount for $path * * @param string $path * @return \OCP\Files\Mount\IMountPoint * @since 8.2.0 */ public function find($path); /** * Find all mounts in $path * * @param string $path * @return \OCP\Files\Mount\IMountPoint[] * @since 8.2.0 */ public function findIn($path); /** * Remove all registered mounts * * @since 8.2.0 */ public function clear(); /** * Find mounts by storage id * * @param string $id * @return \OCP\Files\Mount\IMountPoint[] * @since 8.2.0 */ public function findByStorageId($id); /** * @return \OCP\Files\Mount\IMountPoint[] * @since 8.2.0 */ public function getAll(); /** * Find mounts by numeric storage id * * @param int $id * @return \OCP\Files\Mount\IMountPoint[] * @since 8.2.0 */ public function findByNumericId($id); } public/Files/Mount/IMountPoint.php 0000604 00000005306 15247130450 0013125 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Mount; /** * A storage mounted to folder on the filesystem * @since 8.0.0 */ interface IMountPoint { /** * get complete path to the mount point * * @return string * @since 8.0.0 */ public function getMountPoint(); /** * Set the mountpoint * * @param string $mountPoint new mount point * @since 8.0.0 */ public function setMountPoint($mountPoint); /** * Get the storage that is mounted * * @return \OC\Files\Storage\Storage * @since 8.0.0 */ public function getStorage(); /** * Get the id of the storages * * @return string * @since 8.0.0 */ public function getStorageId(); /** * Get the id of the storages * * @return int * @since 9.1.0 */ public function getNumericStorageId(); /** * Get the path relative to the mountpoint * * @param string $path absolute path to a file or folder * @return string * @since 8.0.0 */ public function getInternalPath($path); /** * Apply a storage wrapper to the mounted storage * * @param callable $wrapper * @since 8.0.0 */ public function wrapStorage($wrapper); /** * Get a mount option * * @param string $name Name of the mount option to get * @param mixed $default Default value for the mount option * @return mixed * @since 8.0.0 */ public function getOption($name, $default); /** * Get all options for the mount * * @return array * @since 8.1.0 */ public function getOptions(); /** * Get the file id of the root of the storage * * @return int * @since 9.1.0 */ public function getStorageRootId(); /** * Get the id of the configured mount * * @return int|null mount id or null if not applicable * @since 9.1.0 */ public function getMountId(); /** * Get the type of mount point, used to distinguish things like shares and external storages * in the web interface * * @return string * @since 12.0.0 */ public function getMountType(); } public/Files/InvalidContentException.php 0000604 00000002241 15247130450 0014371 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/InvalidContentException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for invalid content * @since 6.0.0 */ class InvalidContentException extends \Exception {} public/Files/AlreadyExistsException.php 0000604 00000002250 15247130450 0014231 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/AlreadyExistsException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for already existing files/folders * @since 6.0.0 */ class AlreadyExistsException extends \Exception {} public/Files/FileInfo.php 0000604 00000012221 15247130451 0011264 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files; /** * Interface FileInfo * * @package OCP\Files * @since 7.0.0 */ interface FileInfo { /** * @since 7.0.0 */ const TYPE_FILE = 'file'; /** * @since 7.0.0 */ const TYPE_FOLDER = 'dir'; /** * @const \OCP\Files\FileInfo::SPACE_NOT_COMPUTED Return value for a not computed space value * @since 8.0.0 */ const SPACE_NOT_COMPUTED = -1; /** * @const \OCP\Files\FileInfo::SPACE_UNKNOWN Return value for unknown space value * @since 8.0.0 */ const SPACE_UNKNOWN = -2; /** * @const \OCP\Files\FileInfo::SPACE_UNLIMITED Return value for unlimited space * @since 8.0.0 */ const SPACE_UNLIMITED = -3; /** * @since 9.1.0 */ const MIMETYPE_FOLDER = 'httpd/unix-directory'; /** * @const \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX Return regular expression to test filenames against (blacklisting) * @since 12.0.0 */ const BLACKLIST_FILES_REGEX = '\.(part|filepart)$'; /** * Get the Etag of the file or folder * * @return string * @since 7.0.0 */ public function getEtag(); /** * Get the size in bytes for the file or folder * * @return int * @since 7.0.0 */ public function getSize(); /** * Get the last modified date as timestamp for the file or folder * * @return int * @since 7.0.0 */ public function getMtime(); /** * Get the name of the file or folder * * @return string * @since 7.0.0 */ public function getName(); /** * Get the path relative to the storage * * @return string * @since 7.0.0 */ public function getInternalPath(); /** * Get the absolute path * * @return string * @since 7.0.0 */ public function getPath(); /** * Get the full mimetype of the file or folder i.e. 'image/png' * * @return string * @since 7.0.0 */ public function getMimetype(); /** * Get the first part of the mimetype of the file or folder i.e. 'image' * * @return string * @since 7.0.0 */ public function getMimePart(); /** * Get the storage the file or folder is storage on * * @return \OCP\Files\Storage * @since 7.0.0 */ public function getStorage(); /** * Get the file id of the file or folder * * @return int|null * @since 7.0.0 */ public function getId(); /** * Check whether the file is encrypted * * @return bool * @since 7.0.0 */ public function isEncrypted(); /** * Get the permissions of the file or folder as bitmasked combination of the following constants * \OCP\Constants::PERMISSION_CREATE * \OCP\Constants::PERMISSION_READ * \OCP\Constants::PERMISSION_UPDATE * \OCP\Constants::PERMISSION_DELETE * \OCP\Constants::PERMISSION_SHARE * \OCP\Constants::PERMISSION_ALL * * @return int * @since 7.0.0 - namespace of constants has changed in 8.0.0 */ public function getPermissions(); /** * Check whether this is a file or a folder * * @return \OCP\Files\FileInfo::TYPE_FILE|\OCP\Files\FileInfo::TYPE_FOLDER * @since 7.0.0 */ public function getType(); /** * Check if the file or folder is readable * * @return bool * @since 7.0.0 */ public function isReadable(); /** * Check if a file is writable * * @return bool * @since 7.0.0 */ public function isUpdateable(); /** * Check whether new files or folders can be created inside this folder * * @return bool * @since 8.0.0 */ public function isCreatable(); /** * Check if a file or folder can be deleted * * @return bool * @since 7.0.0 */ public function isDeletable(); /** * Check if a file or folder can be shared * * @return bool * @since 7.0.0 */ public function isShareable(); /** * Check if a file or folder is shared * * @return bool * @since 7.0.0 */ public function isShared(); /** * Check if a file or folder is mounted * * @return bool * @since 7.0.0 */ public function isMounted(); /** * Get the mountpoint the file belongs to * * @return \OCP\Files\Mount\IMountPoint * @since 8.0.0 */ public function getMountPoint(); /** * Get the owner of the file * * @return \OCP\IUser * @since 9.0.0 */ public function getOwner(); /** * Get the stored checksum for this file * * @return string * @since 9.0.0 */ public function getChecksum(); } public/Files/NotPermittedException.php 0000604 00000002234 15247130451 0014071 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/NotPermittedException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for not permitted action * @since 6.0.0 */ class NotPermittedException extends \Exception {} public/Files/IHomeStorage.php 0000604 00000002214 15247130451 0012120 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/Storage interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Interface IHomeStorage * * @package OCP\Files * @since 7.0.0 */ interface IHomeStorage { } public/Files/File.php 0000604 00000004613 15247130451 0010456 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/File interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Interface File * * @package OCP\Files * @since 6.0.0 */ interface File extends Node { /** * Get the content of the file as string * * @return string * @throws \OCP\Files\NotPermittedException * @since 6.0.0 */ public function getContent(); /** * Write to the file from string data * * @param string $data * @throws \OCP\Files\NotPermittedException * @return void * @since 6.0.0 */ public function putContent($data); /** * Get the mimetype of the file * * @return string * @since 6.0.0 */ public function getMimeType(); /** * Open the file as stream, resulting resource can be operated as stream like the result from php's own fopen * * @param string $mode * @return resource * @throws \OCP\Files\NotPermittedException * @since 6.0.0 */ public function fopen($mode); /** * Compute the hash of the file * Type of hash is set with $type and can be anything supported by php's hash_file * * @param string $type * @param bool $raw * @return string * @since 6.0.0 */ public function hash($type, $raw = false); /** * Get the stored checksum for this file * * @return string * @since 9.0.0 * @throws InvalidPathException * @throws NotFoundException */ public function getChecksum(); } public/Files/ForbiddenException.php 0000604 00000002730 15247130451 0013350 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Class ForbiddenException * * @package OCP\Files * @since 9.0.0 */ class ForbiddenException extends \Exception { /** @var bool */ private $retry; /** * @param string $message * @param bool $retry * @param \Exception $previous previous exception for cascading * @since 9.0.0 */ public function __construct($message, $retry, \Exception $previous = null) { parent::__construct($message, 0, $previous); $this->retry = $retry; } /** * @return bool * @since 9.0.0 */ public function getRetry() { return (bool) $this->retry; } } public/Files/StorageInvalidException.php 0000604 00000002237 15247130451 0014371 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/AlreadyExistsException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Storage has invalid configuration * @since 7.0.0 */ class StorageInvalidException extends \Exception { } public/Files/Folder.php 0000604 00000010464 15247130451 0011013 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/Folder interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; use OCP\Files\Search\ISearchQuery; /** * @since 6.0.0 */ interface Folder extends Node { /** * Get the full path of an item in the folder within owncloud's filesystem * * @param string $path relative path of an item in the folder * @return string * @throws \OCP\Files\NotPermittedException * @since 6.0.0 */ public function getFullPath($path); /** * Get the path of an item in the folder relative to the folder * * @param string $path absolute path of an item in the folder * @throws \OCP\Files\NotFoundException * @return string * @since 6.0.0 */ public function getRelativePath($path); /** * check if a node is a (grand-)child of the folder * * @param \OCP\Files\Node $node * @return bool * @since 6.0.0 */ public function isSubNode($node); /** * get the content of this directory * * @throws \OCP\Files\NotFoundException * @return \OCP\Files\Node[] * @since 6.0.0 */ public function getDirectoryListing(); /** * Get the node at $path * * @param string $path relative path of the file or folder * @return \OCP\Files\Node * @throws \OCP\Files\NotFoundException * @since 6.0.0 */ public function get($path); /** * Check if a file or folder exists in the folder * * @param string $path relative path of the file or folder * @return bool * @since 6.0.0 */ public function nodeExists($path); /** * Create a new folder * * @param string $path relative path of the new folder * @return \OCP\Files\Folder * @throws \OCP\Files\NotPermittedException * @since 6.0.0 */ public function newFolder($path); /** * Create a new file * * @param string $path relative path of the new file * @return \OCP\Files\File * @throws \OCP\Files\NotPermittedException * @since 6.0.0 */ public function newFile($path); /** * search for files with the name matching $query * * @param string|ISearchQuery $query * @return \OCP\Files\Node[] * @since 6.0.0 */ public function search($query); /** * search for files by mimetype * $mimetype can either be a full mimetype (image/png) or a wildcard mimetype (image) * * @param string $mimetype * @return \OCP\Files\Node[] * @since 6.0.0 */ public function searchByMime($mimetype); /** * search for files by tag * * @param string|int $tag tag name or tag id * @param string $userId owner of the tags * @return \OCP\Files\Node[] * @since 8.0.0 */ public function searchByTag($tag, $userId); /** * get a file or folder inside the folder by it's internal id * * @param int $id * @return \OCP\Files\Node[] * @since 6.0.0 */ public function getById($id); /** * Get the amount of free space inside the folder * * @return int * @since 6.0.0 */ public function getFreeSpace(); /** * Check if new files or folders can be created within the folder * * @return bool * @since 6.0.0 */ public function isCreatable(); /** * Add a suffix to the name in case the file exists * * @param string $name * @return string * @throws NotPermittedException * @since 8.1.0 */ public function getNonExistingName($name); /** * @param int $limit * @param int $offset * @return \OCP\Files\Node[] * @since 9.1.0 */ public function getRecent($limit, $offset = 0); } public/Files/IMimeTypeLoader.php 0000604 00000002761 15247130451 0012572 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files; /** * Interface IMimeTypeLoader * @package OCP\Files * @since 8.2.0 * * Interface to load mimetypes **/ interface IMimeTypeLoader { /** * Get a mimetype from its ID * * @param int $id * @return string|null * @since 8.2.0 */ public function getMimetypeById($id); /** * Get a mimetype ID, adding the mimetype to the DB if it does not exist * * @param string $mimetype * @return int * @since 8.2.0 */ public function getId($mimetype); /** * Test if a mimetype exists in the database * * @param string $mimetype * @return bool * @since 8.2.0 */ public function exists($mimetype); /** * Clear all loaded mimetypes, allow for re-loading * * @since 8.2.0 */ public function reset(); } public/Files/StorageNotAvailableException.php 0000604 00000005012 15247130451 0015336 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Jesús Macias <jmacias@solidgear.es> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/AlreadyExistsException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; use OC\HintException; /** * Storage is temporarily not available * @since 6.0.0 - since 8.2.1 based on HintException */ class StorageNotAvailableException extends HintException { const STATUS_SUCCESS = 0; const STATUS_ERROR = 1; const STATUS_INDETERMINATE = 2; const STATUS_INCOMPLETE_CONF = 3; const STATUS_UNAUTHORIZED = 4; const STATUS_TIMEOUT = 5; const STATUS_NETWORK_ERROR = 6; /** * StorageNotAvailableException constructor. * * @param string $message * @param int $code * @param \Exception $previous * @since 6.0.0 */ public function __construct($message = '', $code = self::STATUS_ERROR, \Exception $previous = null) { $l = \OC::$server->getL10N('core'); parent::__construct($message, $l->t('Storage is temporarily not available'), $code, $previous); } /** * Get the name for a status code * * @param int $code * @return string * @since 9.0.0 */ public static function getStateCodeName($code) { switch ($code) { case self::STATUS_SUCCESS: return 'ok'; case self::STATUS_ERROR: return 'error'; case self::STATUS_INDETERMINATE: return 'indeterminate'; case self::STATUS_UNAUTHORIZED: return 'unauthorized'; case self::STATUS_TIMEOUT: return 'timeout'; case self::STATUS_NETWORK_ERROR: return 'network error'; default: return 'unknown'; } } } public/Files/IAppData.php 0000604 00000002120 15247130451 0011211 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files; use OCP\Files\SimpleFS\ISimpleRoot; /** * Interface IAppData * * @package OCP\Files * @since 11.0.0 * @internal This interface is experimental and might change for NC12 */ interface IAppData extends ISimpleRoot { } public/Files/InvalidPathException.php 0000604 00000002230 15247130451 0013652 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/InvalidPathException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for invalid path * @since 6.0.0 */ class InvalidPathException extends \Exception {} public/Files/Node.php 0000604 00000016476 15247130451 0010476 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/Node interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Interface Node * * @package OCP\Files * @since 6.0.0 - extends FileInfo was added in 8.0.0 */ interface Node extends FileInfo { /** * Move the file or folder to a new location * * @param string $targetPath the absolute target path * @throws \OCP\Files\NotPermittedException * @return \OCP\Files\Node * @since 6.0.0 */ public function move($targetPath); /** * Delete the file or folder * @return void * @since 6.0.0 */ public function delete(); /** * Cope the file or folder to a new location * * @param string $targetPath the absolute target path * @return \OCP\Files\Node * @since 6.0.0 */ public function copy($targetPath); /** * Change the modified date of the file or folder * If $mtime is omitted the current time will be used * * @param int $mtime (optional) modified date as unix timestamp * @throws \OCP\Files\NotPermittedException * @return void * @since 6.0.0 */ public function touch($mtime = null); /** * Get the storage backend the file or folder is stored on * * @return \OCP\Files\Storage * @throws \OCP\Files\NotFoundException * @since 6.0.0 */ public function getStorage(); /** * Get the full path of the file or folder * * @return string * @since 6.0.0 */ public function getPath(); /** * Get the path of the file or folder relative to the mountpoint of it's storage * * @return string * @since 6.0.0 */ public function getInternalPath(); /** * Get the internal file id for the file or folder * * @return int * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 */ public function getId(); /** * Get metadata of the file or folder * The returned array contains the following values: * - mtime * - size * * @return array * @since 6.0.0 */ public function stat(); /** * Get the modified date of the file or folder as unix timestamp * * @return int * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 */ public function getMTime(); /** * Get the size of the file or folder in bytes * * @return int * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 */ public function getSize(); /** * Get the Etag of the file or folder * The Etag is an string id used to detect changes to a file or folder, * every time the file or folder is changed the Etag will change to * * @return string * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 */ public function getEtag(); /** * Get the permissions of the file or folder as a combination of one or more of the following constants: * - \OCP\Constants::PERMISSION_READ * - \OCP\Constants::PERMISSION_UPDATE * - \OCP\Constants::PERMISSION_CREATE * - \OCP\Constants::PERMISSION_DELETE * - \OCP\Constants::PERMISSION_SHARE * * @return int * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 - namespace of constants has changed in 8.0.0 */ public function getPermissions(); /** * Check if the file or folder is readable * * @return bool * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 */ public function isReadable(); /** * Check if the file or folder is writable * * @return bool * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 */ public function isUpdateable(); /** * Check if the file or folder is deletable * * @return bool * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 */ public function isDeletable(); /** * Check if the file or folder is shareable * * @return bool * @throws InvalidPathException * @throws NotFoundException * @since 6.0.0 */ public function isShareable(); /** * Get the parent folder of the file or folder * * @return Folder * @since 6.0.0 */ public function getParent(); /** * Get the filename of the file or folder * * @return string * @since 6.0.0 */ public function getName(); /** * Acquire a lock on this file or folder. * * A shared (read) lock will prevent any exclusive (write) locks from being created but any number of shared locks * can be active at the same time. * An exclusive lock will prevent any other lock from being created (both shared and exclusive). * * A locked exception will be thrown if any conflicting lock already exists * * Note that this uses mandatory locking, if you acquire an exclusive lock on a file it will block *all* * other operations for that file, even within the same php process. * * Acquiring any lock on a file will also create a shared lock on all parent folders of that file. * * Note that in most cases you won't need to manually manage the locks for any files you're working with, * any filesystem operation will automatically acquire the relevant locks for that operation. * * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException * @since 9.1.0 */ public function lock($type); /** * Check the type of an existing lock. * * A shared lock can be changed to an exclusive lock is there is exactly one shared lock on the file, * an exclusive lock can always be changed to a shared lock since there can only be one exclusive lock int he first place. * * A locked exception will be thrown when these preconditions are not met. * Note that this is also the case if no existing lock exists for the file. * * @param int $targetType \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException * @since 9.1.0 */ public function changeLock($targetType); /** * Release an existing lock. * * This will also free up the shared locks on any parent folder that were automatically acquired when locking the file. * * Note that this method will not give any sort of error when trying to free a lock that doesn't exist. * * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException * @since 9.1.0 */ public function unlock($type); } public/Files/ObjectStore/IObjectStore.php 0000604 00000003450 15247130451 0014354 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\ObjectStore; /** * Interface IObjectStore * * @package OCP\Files\ObjectStore * @since 7.0.0 */ interface IObjectStore { /** * @return string the container or bucket name where objects are stored * @since 7.0.0 */ function getStorageId(); /** * @param string $urn the unified resource name used to identify the object * @return resource stream with the read data * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function readObject($urn); /** * @param string $urn the unified resource name used to identify the object * @param resource $stream stream with the data to write * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function writeObject($urn, $stream); /** * @param string $urn the unified resource name used to identify the object * @return void * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function deleteObject($urn); } public/Files/UnseekableException.php 0000604 00000002144 15247130451 0013531 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Piotr Filiciak <piotr@filiciak.pl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/UnseekableException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for seek problem * @since 9.1.0 */ class UnseekableException extends \Exception {} public/Files/Cache/ICacheEntry.php 0000604 00000005670 15247130451 0012744 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Cache; /** * meta data for a file or folder * * @since 9.0.0 */ interface ICacheEntry { const DIRECTORY_MIMETYPE = 'httpd/unix-directory'; /** * Get the numeric id of a file * * @return int * @since 9.0.0 */ public function getId(); /** * Get the numeric id for the storage * * @return int * @since 9.0.0 */ public function getStorageId(); /** * Get the path of the file relative to the storage root * * @return string * @since 9.0.0 */ public function getPath(); /** * Get the file name * * @return string * @since 9.0.0 */ public function getName(); /** * Get the full mimetype * * @return string * @since 9.0.0 */ public function getMimeType(); /** * Get the first part of the mimetype * * @return string * @since 9.0.0 */ public function getMimePart(); /** * Get the file size in bytes * * @return int * @since 9.0.0 */ public function getSize(); /** * Get the last modified date as unix timestamp * * @return int * @since 9.0.0 */ public function getMTime(); /** * Get the last modified date on the storage as unix timestamp * * Note that when a file is updated we also update the mtime of all parent folders to make it visible to the user which folder has had updates most recently * This can differ from the mtime on the underlying storage which usually only changes when a direct child is added, removed or renamed * * @return int * @since 9.0.0 */ public function getStorageMTime(); /** * Get the etag for the file * * An etag is used for change detection of files and folders, an etag of a file changes whenever the content of the file changes * Etag for folders change whenever a file in the folder has changed * * @return string * @since 9.0.0 */ public function getEtag(); /** * Get the permissions for the file stored as bitwise combination of \OCP\PERMISSION_READ, \OCP\PERMISSION_CREATE * \OCP\PERMISSION_UPDATE, \OCP\PERMISSION_DELETE and \OCP\PERMISSION_SHARE * * @return int * @since 9.0.0 */ public function getPermissions(); /** * Check if the file is encrypted * * @return bool * @since 9.0.0 */ public function isEncrypted(); } public/Files/Cache/IUpdater.php 0000604 00000004125 15247130451 0012315 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Cache; use OCP\Files\Storage\IStorage; /** * Update the cache and propagate changes * * @since 9.0.0 */ interface IUpdater { /** * Get the propagator for etags and mtime for the view the updater works on * * @return IPropagator * @since 9.0.0 */ public function getPropagator(); /** * Propagate etag and mtime changes for the parent folders of $path up to the root of the filesystem * * @param string $path the path of the file to propagate the changes for * @param int|null $time the timestamp to set as mtime for the parent folders, if left out the current time is used * @since 9.0.0 */ public function propagate($path, $time = null); /** * Update the cache for $path and update the size, etag and mtime of the parent folders * * @param string $path * @param int $time * @since 9.0.0 */ public function update($path, $time = null); /** * Remove $path from the cache and update the size, etag and mtime of the parent folders * * @param string $path * @since 9.0.0 */ public function remove($path); /** * Rename a file or folder in the cache and update the size, etag and mtime of the parent folders * * @param IStorage $sourceStorage * @param string $source * @param string $target * @since 9.0.0 */ public function renameFromStorage(IStorage $sourceStorage, $source, $target); } public/Files/Cache/IPropagator.php 0000604 00000002706 15247130451 0013032 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Cache; /** * Propagate etags and mtimes within the storage * * @since 9.0.0 */ interface IPropagator { /** * Mark the beginning of a propagation batch * * Note that not all cache setups support propagation in which case this will be a noop * * Batching for cache setups that do support it has to be explicit since the cache state is not fully consistent * before the batch is committed. * * @since 9.1.0 */ public function beginBatch(); /** * Commit the active propagation batch * * @since 9.1.0 */ public function commitBatch(); /** * @param string $internalPath * @param int $time * @since 9.0.0 */ public function propagateChange($internalPath, $time); } public/Files/Cache/ICache.php 0000604 00000017763 15247130451 0011730 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Cache; use OCP\Files\Search\ISearchQuery; /** * Metadata cache for a storage * * The cache stores the metadata for all files and folders in a storage and is kept up to date trough the following mechanisms: * * - Scanner: scans the storage and updates the cache where needed * - Watcher: checks for changes made to the filesystem outside of the ownCloud instance and rescans files and folder when a change is detected * - Updater: listens to changes made to the filesystem inside of the ownCloud instance and updates the cache where needed * - ChangePropagator: updates the mtime and etags of parent folders whenever a change to the cache is made to the cache by the updater * * @since 9.0.0 */ interface ICache { const NOT_FOUND = 0; const PARTIAL = 1; //only partial data available, file not cached in the database const SHALLOW = 2; //folder in cache, but not all child files are completely scanned const COMPLETE = 3; /** * Get the numeric storage id for this cache's storage * * @return int * @since 9.0.0 */ public function getNumericStorageId(); /** * get the stored metadata of a file or folder * * @param string | int $file either the path of a file or folder or the file id for a file or folder * @return ICacheEntry|false the cache entry or false if the file is not found in the cache * @since 9.0.0 */ public function get($file); /** * get the metadata of all files stored in $folder * * Only returns files one level deep, no recursion * * @param string $folder * @return ICacheEntry[] * @since 9.0.0 */ public function getFolderContents($folder); /** * get the metadata of all files stored in $folder * * Only returns files one level deep, no recursion * * @param int $fileId the file id of the folder * @return ICacheEntry[] * @since 9.0.0 */ public function getFolderContentsById($fileId); /** * store meta data for a file or folder * This will automatically call either insert or update depending on if the file exists * * @param string $file * @param array $data * * @return int file id * @throws \RuntimeException * @since 9.0.0 */ public function put($file, array $data); /** * insert meta data for a new file or folder * * @param string $file * @param array $data * * @return int file id * @throws \RuntimeException * @since 9.0.0 */ public function insert($file, array $data); /** * update the metadata of an existing file or folder in the cache * * @param int $id the fileid of the existing file or folder * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged * @since 9.0.0 */ public function update($id, array $data); /** * get the file id for a file * * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file * * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing * * @param string $file * @return int * @since 9.0.0 */ public function getId($file); /** * get the id of the parent folder of a file * * @param string $file * @return int * @since 9.0.0 */ public function getParentId($file); /** * check if a file is available in the cache * * @param string $file * @return bool * @since 9.0.0 */ public function inCache($file); /** * remove a file or folder from the cache * * when removing a folder from the cache all files and folders inside the folder will be removed as well * * @param string $file * @since 9.0.0 */ public function remove($file); /** * Move a file or folder in the cache * * @param string $source * @param string $target * @since 9.0.0 */ public function move($source, $target); /** * Move a file or folder in the cache * * Note that this should make sure the entries are removed from the source cache * * @param \OCP\Files\Cache\ICache $sourceCache * @param string $sourcePath * @param string $targetPath * @throws \OC\DatabaseException * @since 9.0.0 */ public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath); /** * Get the scan status of a file * * - ICache::NOT_FOUND: File is not in the cache * - ICache::PARTIAL: File is not stored in the cache but some incomplete data is known * - ICache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned * - ICache::COMPLETE: The file or folder, with all it's children) are fully scanned * * @param string $file * * @return int ICache::NOT_FOUND, ICache::PARTIAL, ICache::SHALLOW or ICache::COMPLETE * @since 9.0.0 */ public function getStatus($file); /** * search for files matching $pattern, files are matched if their filename matches the search pattern * * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%') * @return ICacheEntry[] an array of cache entries where the name matches the search pattern * @since 9.0.0 * @deprecated 9.0.0 due to lack of pagination, not all backends might implement this */ public function search($pattern); /** * search for files by mimetype * * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image') * where it will search for all mimetypes in the group ('image/*') * @return ICacheEntry[] an array of cache entries where the mimetype matches the search * @since 9.0.0 * @deprecated 9.0.0 due to lack of pagination, not all backends might implement this */ public function searchByMime($mimetype); /** * Search for files with a flexible query * * @param ISearchQuery $query * @return ICacheEntry[] * @throw \InvalidArgumentException if the cache is unable to perform the query * @since 12.0.0 */ public function searchQuery(ISearchQuery $query); /** * Search for files by tag of a given users. * * Note that every user can tag files differently. * * @param string|int $tag name or tag id * @param string $userId owner of the tags * @return ICacheEntry[] file data * @since 9.0.0 * @deprecated 9.0.0 due to lack of pagination, not all backends might implement this */ public function searchByTag($tag, $userId); /** * find a folder in the cache which has not been fully scanned * * If multiple incomplete folders are in the cache, the one with the highest id will be returned, * use the one with the highest id gives the best result with the background scanner, since that is most * likely the folder where we stopped scanning previously * * @return string|bool the path of the folder or false when no folder matched * @since 9.0.0 */ public function getIncomplete(); /** * get the path of a file on this storage by it's file id * * @param int $id the file id of the file or folder to search * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache * @since 9.0.0 */ public function getPathById($id); /** * normalize the given path for usage in the cache * * @param string $path * @return string * @since 9.0.0 */ public function normalize($path); } public/Files/Cache/IScanner.php 0000604 00000004735 15247130451 0012311 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Cache; /** * Scan files from the storage and save to the cache * * @since 9.0.0 */ interface IScanner { const SCAN_RECURSIVE_INCOMPLETE = 2; // only recursive into not fully scanned folders const SCAN_RECURSIVE = true; const SCAN_SHALLOW = false; const REUSE_NONE = 0; const REUSE_ETAG = 1; const REUSE_SIZE = 2; /** * scan a single file and store it in the cache * * @param string $file * @param int $reuseExisting * @param int $parentId * @param array | null $cacheData existing data in the cache for the file to be scanned * @param bool $lock set to false to disable getting an additional read lock during scanning * @return array an array of metadata of the scanned file * @throws \OC\ServerNotAvailableException * @throws \OCP\Lock\LockedException * @since 9.0.0 */ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true); /** * scan a folder and all its children * * @param string $path * @param bool $recursive * @param int $reuse * @param bool $lock set to false to disable getting an additional read lock during scanning * @return array an array of the meta data of the scanned file or folder * @since 9.0.0 */ public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true); /** * check if the file should be ignored when scanning * NOTE: files with a '.part' extension are ignored as well! * prevents unfinished put requests to be scanned * * @param string $file * @return boolean * @since 9.0.0 */ public static function isPartialFile($file); /** * walk over any folders that are not fully scanned yet and scan them * * @since 9.0.0 */ public function backgroundScan(); } public/Files/Cache/IWatcher.php 0000604 00000004321 15247130451 0012304 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Files\Cache; /** * check the storage backends for updates and change the cache accordingly * * @since 9.0.0 */ interface IWatcher { const CHECK_NEVER = 0; // never check the underlying filesystem for updates const CHECK_ONCE = 1; // check the underlying filesystem for updates once every request for each file const CHECK_ALWAYS = 2; // always check the underlying filesystem for updates /** * @param int $policy either IWatcher::CHECK_NEVER, IWatcher::CHECK_ONCE, IWatcher::CHECK_ALWAYS * @since 9.0.0 */ public function setPolicy($policy); /** * @return int either IWatcher::CHECK_NEVER, IWatcher::CHECK_ONCE, IWatcher::CHECK_ALWAYS * @since 9.0.0 */ public function getPolicy(); /** * check $path for updates and update if needed * * @param string $path * @param ICacheEntry|null $cachedEntry * @return boolean true if path was updated * @since 9.0.0 */ public function checkUpdate($path, $cachedEntry = null); /** * Update the cache for changes to $path * * @param string $path * @param ICacheEntry $cachedData * @since 9.0.0 */ public function update($path, $cachedData); /** * Check if the cache for $path needs to be updated * * @param string $path * @param ICacheEntry $cachedData * @return bool * @since 9.0.0 */ public function needsUpdate($path, $cachedData); /** * remove deleted files in $path from the cache * * @param string $path * @since 9.0.0 */ public function cleanFolder($path); } public/Files/InvalidDirectoryException.php 0000604 00000001730 15247130451 0014726 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files; /** * Class InvalidDirectoryException * * @package OCP\Files * @since 9.2.0 */ class InvalidDirectoryException extends InvalidPathException { } public/Files/InvalidCharacterInPathException.php 0000604 00000002272 15247130451 0015764 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/InvalidCharacterInPathException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for invalid path * @since 8.1.0 */ class InvalidCharacterInPathException extends InvalidPathException { } public/Files/SimpleFS/ISimpleFile.php 0000604 00000003722 15247130451 0013423 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\SimpleFS; use OCP\Files\NotPermittedException; /** * Interface ISimpleFile * * @package OCP\Files\SimpleFS * @since 11.0.0 * @internal This interface is experimental and might change for NC12 */ interface ISimpleFile { /** * Get the name * * @return string * @since 11.0.0 */ public function getName(); /** * Get the size in bytes * * @return int * @since 11.0.0 */ public function getSize(); /** * Get the ETag * * @return string * @since 11.0.0 */ public function getETag(); /** * Get the last modification time * * @return int * @since 11.0.0 */ public function getMTime(); /** * Get the content * * @return string * @since 11.0.0 */ public function getContent(); /** * Overwrite the file * * @param string $data * @throws NotPermittedException * @since 11.0.0 */ public function putContent($data); /** * Delete the file * * @throws NotPermittedException * @since 11.0.0 */ public function delete(); /** * Get the MimeType * * @return string * @since 11.0.0 */ public function getMimeType(); } public/Files/SimpleFS/ISimpleFolder.php 0000604 00000004022 15247130451 0013751 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\SimpleFS; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; /** * Interface ISimpleFolder * * @package OCP\Files\SimpleFS * @since 11.0.0 * @internal This interface is experimental and might change for NC12 */ interface ISimpleFolder { /** * Get all the files in a folder * * @return ISimpleFile[] * @since 11.0.0 */ public function getDirectoryListing(); /** * Check if a file with $name exists * * @param string $name * @return bool * @since 11.0.0 */ public function fileExists($name); /** * Get the file named $name from the folder * * @param string $name * @return ISimpleFile * @throws NotFoundException * @since 11.0.0 */ public function getFile($name); /** * Creates a new file with $name in the folder * * @param string $name * @return ISimpleFile * @throws NotPermittedException * @since 11.0.0 */ public function newFile($name); /** * Remove the folder and all the files in it * * @throws NotPermittedException * @since 11.0.0 */ public function delete(); /** * Get the folder name * * @return string * @since 11.0.0 */ public function getName(); } public/Files/SimpleFS/ISimpleRoot.php 0000604 00000003350 15247130451 0013464 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Files\SimpleFS; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; /** * Interface ISimpleRoot * * @package OCP\Files\SimpleFS * @since 11.0.0 * @internal This interface is experimental and might change for NC12 */ interface ISimpleRoot { /** * Get the folder with name $name * * @param string $name * @return ISimpleFolder * @throws NotFoundException * @throws \RuntimeException * @since 11.0.0 */ public function getFolder($name); /** * Get all the Folders * * @return ISimpleFolder[] * @throws NotFoundException * @throws \RuntimeException * @since 11.0.0 */ public function getDirectoryListing(); /** * Create a new folder named $name * * @param string $name * @return ISimpleFolder * @throws NotPermittedException * @throws \RuntimeException * @since 11.0.0 */ public function newFolder($name); } public/Files/Storage.php 0000604 00000025710 15247130451 0011204 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Michael Roth <michael.roth@rz.uni-augsburg.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/Storage interface */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; use OCP\Files\Storage\IStorage; use OCP\Lock\ILockingProvider; /** * Provide a common interface to all different storage options * * All paths passed to the storage are relative to the storage and should NOT have a leading slash. * * @since 6.0.0 * @deprecated 9.0.0 use \OCP\Files\Storage\IStorage instead */ interface Storage extends IStorage { /** * $parameters is a free form array with the configuration options needed to construct the storage * * @param array $parameters * @since 6.0.0 */ public function __construct($parameters); /** * Get the identifier for the storage, * the returned id should be the same for every storage object that is created with the same parameters * and two storage objects with the same id should refer to two storages that display the same files. * * @return string * @since 6.0.0 */ public function getId(); /** * see http://php.net/manual/en/function.mkdir.php * implementations need to implement a recursive mkdir * * @param string $path * @return bool * @since 6.0.0 */ public function mkdir($path); /** * see http://php.net/manual/en/function.rmdir.php * * @param string $path * @return bool * @since 6.0.0 */ public function rmdir($path); /** * see http://php.net/manual/en/function.opendir.php * * @param string $path * @return resource|false * @since 6.0.0 */ public function opendir($path); /** * see http://php.net/manual/en/function.is-dir.php * * @param string $path * @return bool * @since 6.0.0 */ public function is_dir($path); /** * see http://php.net/manual/en/function.is-file.php * * @param string $path * @return bool * @since 6.0.0 */ public function is_file($path); /** * see http://php.net/manual/en/function.stat.php * only the following keys are required in the result: size and mtime * * @param string $path * @return array|false * @since 6.0.0 */ public function stat($path); /** * see http://php.net/manual/en/function.filetype.php * * @param string $path * @return string|false * @since 6.0.0 */ public function filetype($path); /** * see http://php.net/manual/en/function.filesize.php * The result for filesize when called on a folder is required to be 0 * * @param string $path * @return int|false * @since 6.0.0 */ public function filesize($path); /** * check if a file can be created in $path * * @param string $path * @return bool * @since 6.0.0 */ public function isCreatable($path); /** * check if a file can be read * * @param string $path * @return bool * @since 6.0.0 */ public function isReadable($path); /** * check if a file can be written to * * @param string $path * @return bool * @since 6.0.0 */ public function isUpdatable($path); /** * check if a file can be deleted * * @param string $path * @return bool * @since 6.0.0 */ public function isDeletable($path); /** * check if a file can be shared * * @param string $path * @return bool * @since 6.0.0 */ public function isSharable($path); /** * get the full permissions of a path. * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php * * @param string $path * @return int * @since 6.0.0 */ public function getPermissions($path); /** * see http://php.net/manual/en/function.file_exists.php * * @param string $path * @return bool * @since 6.0.0 */ public function file_exists($path); /** * see http://php.net/manual/en/function.filemtime.php * * @param string $path * @return int|false * @since 6.0.0 */ public function filemtime($path); /** * see http://php.net/manual/en/function.file_get_contents.php * * @param string $path * @return string|false * @since 6.0.0 */ public function file_get_contents($path); /** * see http://php.net/manual/en/function.file_put_contents.php * * @param string $path * @param string $data * @return bool * @since 6.0.0 */ public function file_put_contents($path, $data); /** * see http://php.net/manual/en/function.unlink.php * * @param string $path * @return bool * @since 6.0.0 */ public function unlink($path); /** * see http://php.net/manual/en/function.rename.php * * @param string $path1 * @param string $path2 * @return bool * @since 6.0.0 */ public function rename($path1, $path2); /** * see http://php.net/manual/en/function.copy.php * * @param string $path1 * @param string $path2 * @return bool * @since 6.0.0 */ public function copy($path1, $path2); /** * see http://php.net/manual/en/function.fopen.php * * @param string $path * @param string $mode * @return resource|false * @since 6.0.0 */ public function fopen($path, $mode); /** * get the mimetype for a file or folder * The mimetype for a folder is required to be "httpd/unix-directory" * * @param string $path * @return string|false * @since 6.0.0 */ public function getMimeType($path); /** * see http://php.net/manual/en/function.hash-file.php * * @param string $type * @param string $path * @param bool $raw * @return string|false * @since 6.0.0 */ public function hash($type, $path, $raw = false); /** * see http://php.net/manual/en/function.free_space.php * * @param string $path * @return int|false * @since 6.0.0 */ public function free_space($path); /** * search for occurrences of $query in file names * * @param string $query * @return array|false * @since 6.0.0 */ public function search($query); /** * see http://php.net/manual/en/function.touch.php * If the backend does not support the operation, false should be returned * * @param string $path * @param int $mtime * @return bool * @since 6.0.0 */ public function touch($path, $mtime = null); /** * get the path to a local version of the file. * The local version of the file can be temporary and doesn't have to be persistent across requests * * @param string $path * @return string|false * @since 6.0.0 */ public function getLocalFile($path); /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @return bool * @since 6.0.0 * * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. * returning true for other changes in the folder is optional */ public function hasUpdated($path, $time); /** * get the ETag for a file or folder * * @param string $path * @return string|false * @since 6.0.0 */ public function getETag($path); /** * Returns whether the storage is local, which means that files * are stored on the local filesystem instead of remotely. * Calling getLocalFile() for local storages should always * return the local files, whereas for non-local storages * it might return a temporary file. * * @return bool true if the files are stored locally, false otherwise * @since 7.0.0 */ public function isLocal(); /** * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class * * @param string $class * @return bool * @since 7.0.0 */ public function instanceOfStorage($class); /** * A custom storage implementation can return an url for direct download of a give file. * * For now the returned array can hold the parameter url - in future more attributes might follow. * * @param string $path * @return array|false * @since 8.0.0 */ public function getDirectDownload($path); /** * @param string $path the path of the target folder * @param string $fileName the name of the file itself * @return void * @throws InvalidPathException * @since 8.1.0 */ public function verifyPath($path, $fileName); /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool * @since 8.1.0 */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath); /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool * @since 8.1.0 */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath); /** * @param string $path The path of the file to acquire the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException * @since 8.1.0 */ public function acquireLock($path, $type, ILockingProvider $provider); /** * @param string $path The path of the file to acquire the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException * @since 8.1.0 */ public function releaseLock($path, $type, ILockingProvider $provider); /** * @param string $path The path of the file to change the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException * @since 8.1.0 */ public function changeLock($path, $type, ILockingProvider $provider); /** * Test a storage for availability * * @since 8.2.0 * @return bool */ public function test(); /** * @since 8.2.0 * @return array [ available, last_checked ] */ public function getAvailability(); /** * @since 8.2.0 * @param bool $isAvailable */ public function setAvailability($isAvailable); public function needsPartFile(); } public/Files/NotEnoughSpaceException.php 0000604 00000002234 15247130451 0014335 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/NotEnoughSpaceException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for not enough space * @since 6.0.0 */ class NotEnoughSpaceException extends \Exception {} public/Files/EntityTooLargeException.php 0000604 00000002242 15247130451 0014363 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files/EntityTooLargeException class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Files; /** * Exception for too large entity * @since 6.0.0 */ class EntityTooLargeException extends \Exception {} public/IConfig.php 0000604 00000016463 15247130451 0010061 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Config interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Access to all the configuration options ownCloud offers * @since 6.0.0 */ interface IConfig { /** * @since 8.2.0 */ const SENSITIVE_VALUE = '***REMOVED SENSITIVE VALUE***'; /** * Sets and deletes system wide values * * @param array $configs Associative array with `key => value` pairs * If value is null, the config key will be deleted * @since 8.0.0 */ public function setSystemValues(array $configs); /** * Sets a new system wide value * * @param string $key the key of the value, under which will be saved * @param mixed $value the value that should be stored * @since 8.0.0 */ public function setSystemValue($key, $value); /** * Looks up a system wide defined value * * @param string $key the key of the value, under which it was saved * @param mixed $default the default value to be returned if the value isn't set * @return mixed the value or $default * @since 6.0.0 - parameter $default was added in 7.0.0 */ public function getSystemValue($key, $default = ''); /** * Looks up a system wide defined value and filters out sensitive data * * @param string $key the key of the value, under which it was saved * @param mixed $default the default value to be returned if the value isn't set * @return mixed the value or $default * @since 8.2.0 */ public function getFilteredSystemValue($key, $default = ''); /** * Delete a system wide defined value * * @param string $key the key of the value, under which it was saved * @since 8.0.0 */ public function deleteSystemValue($key); /** * Get all keys stored for an app * * @param string $appName the appName that we stored the value under * @return string[] the keys stored for the app * @since 8.0.0 */ public function getAppKeys($appName); /** * Writes a new app wide value * * @param string $appName the appName that we want to store the value under * @param string|float|int $key the key of the value, under which will be saved * @param string $value the value that should be stored * @return void * @since 6.0.0 */ public function setAppValue($appName, $key, $value); /** * Looks up an app wide defined value * * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @param string $default the default value to be returned if the value isn't set * @return string the saved value * @since 6.0.0 - parameter $default was added in 7.0.0 */ public function getAppValue($appName, $key, $default = ''); /** * Delete an app wide defined value * * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @since 8.0.0 */ public function deleteAppValue($appName, $key); /** * Removes all keys in appconfig belonging to the app * * @param string $appName the appName the configs are stored under * @since 8.0.0 */ public function deleteAppValues($appName); /** * Set a user defined value * * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we want to store the value under * @param string $key the key under which the value is being stored * @param string $value the value that you want to store * @param string $preCondition only update if the config value was previously the value passed as $preCondition * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met * @throws \UnexpectedValueException when trying to store an unexpected value * @since 6.0.0 - parameter $precondition was added in 8.0.0 */ public function setUserValue($userId, $appName, $key, $value, $preCondition = null); /** * Shortcut for getting a user defined value * * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we stored the value under * @param string $key the key under which the value is being stored * @param mixed $default the default value to be returned if the value isn't set * @return string * @since 6.0.0 - parameter $default was added in 7.0.0 */ public function getUserValue($userId, $appName, $key, $default = ''); /** * Fetches a mapped list of userId -> value, for a specified app and key and a list of user IDs. * * @param string $appName app to get the value for * @param string $key the key to get the value for * @param array $userIds the user IDs to fetch the values for * @return array Mapped values: userId => value * @since 8.0.0 */ public function getUserValueForUsers($appName, $key, $userIds); /** * Get the keys of all stored by an app for the user * * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we stored the value under * @return string[] * @since 8.0.0 */ public function getUserKeys($userId, $appName); /** * Delete a user value * * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we stored the value under * @param string $key the key under which the value is being stored * @since 8.0.0 */ public function deleteUserValue($userId, $appName, $key); /** * Delete all user values * * @param string $userId the userId of the user that we want to remove all values from * @since 8.0.0 */ public function deleteAllUserValues($userId); /** * Delete all user related values of one app * * @param string $appName the appName of the app that we want to remove all values from * @since 8.0.0 */ public function deleteAppFromAllUsers($appName); /** * Determines the users that have the given value set for a specific app-key-pair * * @param string $appName the app to get the user for * @param string $key the key to get the user for * @param string $value the value to get the user for * @return array of user IDs * @since 8.0.0 */ public function getUsersForUserValue($appName, $key, $value); } public/Search/Result.php 0000604 00000003730 15247130451 0011217 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Search; /** * The generic result of a search * @since 7.0.0 */ class Result { /** * A unique identifier for the result, usually given as the item ID in its * corresponding application. * @var string * @since 7.0.0 */ public $id; /** * The name of the item returned; this will be displayed in the search * results. * @var string * @since 7.0.0 */ public $name; /** * URL to the application item. * @var string * @since 7.0.0 */ public $link; /** * The type of search result returned; for consistency, name this the same * as the class name (e.g. \OC\Search\File -> 'file') in lowercase. * @var string * @since 7.0.0 */ public $type = 'generic'; /** * Create a new search result * @param string $id unique identifier from application: '[app_name]/[item_identifier_in_app]' * @param string $name displayed text of result * @param string $link URL to the result within its app * @since 7.0.0 */ public function __construct($id = null, $name = null, $link = null) { $this->id = $id; $this->name = $name; $this->link = $link; } } public/Search/Provider.php 0000604 00000004633 15247130451 0011536 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Search; /** * Provides a template for search functionality throughout ownCloud; * @since 7.0.0 */ abstract class Provider { /** * @since 8.0.0 */ const OPTION_APPS = 'apps'; /** * List of options * @var array * @since 7.0.0 */ protected $options; /** * Constructor * @param array $options as key => value * @since 7.0.0 - default value for $options was added in 8.0.0 */ public function __construct($options = array()) { $this->options = $options; } /** * get a value from the options array or null * @param string $key * @return mixed * @since 8.0.0 */ public function getOption($key) { if (is_array($this->options) && isset($this->options[$key])) { return $this->options[$key]; } else { return null; } } /** * checks if the given apps and the apps this provider has results for intersect * returns true if the given array is empty (all apps) * or if this provider does not have a list of apps it provides results for (legacy search providers) * or if the two above arrays have elements in common (intersect) * @param string[] $apps * @return bool * @since 8.0.0 */ public function providesResultsFor(array $apps = array()) { $forApps = $this->getOption(self::OPTION_APPS); return empty($apps) || empty($forApps) || array_intersect($forApps, $apps); } /** * Search for $query * @param string $query * @return array An array of OCP\Search\Result's * @since 7.0.0 */ abstract public function search($query); } public/Search/PagedProvider.php 0000604 00000003355 15247130451 0012477 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Search; /** * Provides a template for search functionality throughout ownCloud; * @since 8.0.0 */ abstract class PagedProvider extends Provider { /** * show all results * @since 8.0.0 */ const SIZE_ALL = 0; /** * Constructor * @param array $options * @since 8.0.0 */ public function __construct($options) { $this->options = $options; } /** * Search for $query * @param string $query * @return array An array of OCP\Search\Result's * @since 8.0.0 */ public function search($query) { // old apps might assume they get all results, so we use SIZE_ALL $this->searchPaged($query, 1, self::SIZE_ALL); } /** * Search for $query * @param string $query * @param int $page pages start at page 1 * @param int $size 0 = SIZE_ALL * @return array An array of OCP\Search\Result's * @since 8.0.0 */ abstract public function searchPaged($query, $page, $size); } public/ISearch.php 0000604 00000004352 15247130451 0010053 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Small Interface for Search * @since 7.0.0 */ interface ISearch { /** * Search all providers for $query * @param string $query * @param string[] $inApps optionally limit results to the given apps * @return array An array of OCP\Search\Result's * @deprecated 8.0.0 use searchPaged() with page and size * @since 7.0.0 - parameter $inApps was added in 8.0.0 */ public function search($query, array $inApps = array()); /** * Search all providers for $query * @param string $query * @param string[] $inApps optionally limit results to the given apps * @param int $page pages start at page 1 * @param int $size * @return array An array of OCP\Search\Result's * @since 8.0.0 */ public function searchPaged($query, array $inApps = array(), $page = 1, $size = 30); /** * Register a new search provider to search with * @param string $class class name of a OCP\Search\Provider * @param array $options optional * @since 7.0.0 */ public function registerProvider($class, array $options = array()); /** * Remove one existing search provider * @param string $provider class name of a OCP\Search\Provider * @since 7.0.0 */ public function removeProvider($provider); /** * Remove all registered search providers * @since 7.0.0 */ public function clearProviders(); } public/IImage.php 0000604 00000010023 15247130451 0007660 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Class for basic image manipulation * @since 8.1.0 */ interface IImage { /** * Determine whether the object contains an image resource. * * @return bool * @since 8.1.0 */ public function valid(); /** * Returns the MIME type of the image or an empty string if no image is loaded. * * @return string * @since 8.1.0 */ public function mimeType(); /** * Returns the width of the image or -1 if no image is loaded. * * @return int * @since 8.1.0 */ public function width(); /** * Returns the height of the image or -1 if no image is loaded. * * @return int * @since 8.1.0 */ public function height(); /** * Returns the width when the image orientation is top-left. * * @return int * @since 8.1.0 */ public function widthTopLeft(); /** * Returns the height when the image orientation is top-left. * * @return int * @since 8.1.0 */ public function heightTopLeft(); /** * Outputs the image. * * @param string $mimeType * @return bool * @since 8.1.0 */ public function show($mimeType = null); /** * Saves the image. * * @param string $filePath * @param string $mimeType * @return bool * @since 8.1.0 */ public function save($filePath = null, $mimeType = null); /** * @return resource Returns the image resource in any. * @since 8.1.0 */ public function resource(); /** * @return string Returns the raw image data. * @since 8.1.0 */ public function data(); /** * (I'm open for suggestions on better method name ;) * Get the orientation based on EXIF data. * * @return int The orientation or -1 if no EXIF data is available. * @since 8.1.0 */ public function getOrientation(); /** * (I'm open for suggestions on better method name ;) * Fixes orientation based on EXIF data. * * @return bool * @since 8.1.0 */ public function fixOrientation(); /** * Resizes the image preserving ratio. * * @param integer $maxSize The maximum size of either the width or height. * @return bool * @since 8.1.0 */ public function resize($maxSize); /** * @param int $width * @param int $height * @return bool * @since 8.1.0 */ public function preciseResize($width, $height); /** * Crops the image to the middle square. If the image is already square it just returns. * * @param int $size maximum size for the result (optional) * @return bool for success or failure * @since 8.1.0 */ public function centerCrop($size = 0); /** * Crops the image from point $x$y with dimension $wx$h. * * @param int $x Horizontal position * @param int $y Vertical position * @param int $w Width * @param int $h Height * @return bool for success or failure * @since 8.1.0 */ public function crop($x, $y, $w, $h); /** * Resizes the image to fit within a boundary while preserving ratio. * * @param integer $maxWidth * @param integer $maxHeight * @return bool * @since 8.1.0 */ public function fitIn($maxWidth, $maxHeight); /** * Shrinks the image to fit within a boundary while preserving ratio. * * @param integer $maxWidth * @param integer $maxHeight * @return bool * @since 8.1.0 */ public function scaleDownToFit($maxWidth, $maxHeight); } public/Share.php 0000604 00000036333 15247130451 0007603 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Michael Kuhn <suraia@ikkoku.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sam Tuke <mail@samtuke.com> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Share Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides the ability for apps to share their content between users. * Apps must create a backend class that implements OCP\Share_Backend and register it with this class. * * It provides the following hooks: * - post_shared * @since 5.0.0 */ class Share extends \OC\Share\Constants { /** * Register a sharing backend class that implements OCP\Share_Backend for an item type * @param string $itemType Item type * @param string $class Backend class * @param string $collectionOf (optional) Depends on item type * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files * @return boolean true if backend is registered or false if error * @since 5.0.0 */ public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) { return \OC\Share\Share::registerBackend($itemType, $class, $collectionOf, $supportedFileExtensions); } /** * Check if the Share API is enabled * @return boolean true if enabled or false * * The Share API is enabled by default if not configured * @since 5.0.0 */ public static function isEnabled() { return \OC\Share\Share::isEnabled(); } /** * Find which users can access a shared item * @param string $path to the file * @param string $ownerUser owner of the file * @param bool $includeOwner include owner to the list of users with access to the file * @param bool $returnUserPaths Return an array with the user => path map * @param bool $recursive take parent folders into account * @return array * @note $path needs to be relative to user data dir, e.g. 'file.txt' * not '/admin/files/file.txt' * @since 5.0.0 - $recursive was added in 9.0.0 */ public static function getUsersSharingFile($path, $ownerUser, $includeOwner = false, $returnUserPaths = false, $recursive = true) { return \OC\Share\Share::getUsersSharingFile( $path, $ownerUser, \OC::$server->getUserManager(), \OC::$server->getLogger(), $includeOwner, $returnUserPaths, $recursive ); } /** * Get the items of item type shared with the current user * @param string $itemType * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters (optional) * @param int $limit Number of items to return (optional) Returns all by default * @param bool $includeCollections (optional) * @return mixed Return depends on format * @since 5.0.0 */ public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { return \OC\Share\Share::getItemsSharedWith($itemType, $format, $parameters, $limit, $includeCollections); } /** * Get the items of item type shared with a user * @param string $itemType * @param string $user for which user we want the shares * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters (optional) * @param int $limit Number of items to return (optional) Returns all by default * @param bool $includeCollections (optional) * @return mixed Return depends on format * @since 7.0.0 */ public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { return \OC\Share\Share::getItemsSharedWithUser($itemType, $user, $format, $parameters, $limit, $includeCollections); } /** * Get the item of item type shared with the current user * @param string $itemType * @param string $itemTarget * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters (optional) * @param bool $includeCollections (optional) * @return mixed Return depends on format * @since 5.0.0 */ public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { return \OC\Share\Share::getItemSharedWith($itemType, $itemTarget, $format, $parameters, $includeCollections); } /** * Get the item of item type shared with a given user by source * @param string $itemType * @param string $itemSource * @param string $user User to whom the item was shared * @param string $owner Owner of the share * @return array Return list of items with file_target, permissions and expiration * @since 6.0.0 - parameter $owner was added in 8.0.0 */ public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null) { return \OC\Share\Share::getItemSharedWithUser($itemType, $itemSource, $user, $owner); } /** * Get the item of item type shared with the current user by source * @param string $itemType * @param string $itemSource * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters * @param bool $includeCollections * @return array * @since 5.0.0 */ public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { return \OC\Share\Share::getItemSharedWithBySource($itemType, $itemSource, $format, $parameters, $includeCollections); } /** * Get the item of item type shared by a link * @param string $itemType * @param string $itemSource * @param string $uidOwner Owner of link * @return array * @since 5.0.0 */ public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) { return \OC\Share\Share::getItemSharedWithByLink($itemType, $itemSource, $uidOwner); } /** * Based on the given token the share information will be returned - password protected shares will be verified * @param string $token * @param bool $checkPasswordProtection * @return array|bool false will be returned in case the token is unknown or unauthorized * @since 5.0.0 - parameter $checkPasswordProtection was added in 7.0.0 */ public static function getShareByToken($token, $checkPasswordProtection = true) { return \OC\Share\Share::getShareByToken($token, $checkPasswordProtection); } /** * resolves reshares down to the last real share * @param array $linkItem * @return array file owner * @since 6.0.0 */ public static function resolveReShare($linkItem) { return \OC\Share\Share::resolveReShare($linkItem); } /** * Get the shared items of item type owned by the current user * @param string $itemType * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters * @param int $limit Number of items to return (optional) Returns all by default * @param bool $includeCollections * @return mixed Return depends on format * @since 5.0.0 */ public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { return \OC\Share\Share::getItemsShared($itemType, $format, $parameters, $limit, $includeCollections); } /** * Get the shared item of item type owned by the current user * @param string $itemType * @param string $itemSource * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters * @param bool $includeCollections * @return mixed Return depends on format * @since 5.0.0 */ public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { return \OC\Share\Share::getItemShared($itemType, $itemSource, $format, $parameters, $includeCollections); } /** * Get all users an item is shared with * @param string $itemType * @param string $itemSource * @param string $uidOwner * @param bool $includeCollections * @param bool $checkExpireDate * @return array Return array of users * @since 5.0.0 - parameter $checkExpireDate was added in 7.0.0 */ public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) { return \OC\Share\Share::getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections, $checkExpireDate); } /** * Share an item with a user, group, or via private link * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $shareWith User or group the item is being shared with * @param int $permissions CRUDS * @param string $itemSourceName * @param \DateTime $expirationDate * @param bool $passwordChanged * @return bool|string Returns true on success or false on failure, Returns token on success for links * @throws \OC\HintException when the share type is remote and the shareWith is invalid * @throws \Exception * @since 5.0.0 - parameter $itemSourceName was added in 6.0.0, parameter $expirationDate was added in 7.0.0, parameter $passwordChanged added in 9.0.0 */ public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) { return \OC\Share\Share::shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName, $expirationDate, $passwordChanged); } /** * Unshare an item from a user, group, or delete a private link * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $shareWith User or group the item is being shared with * @param string $owner owner of the share, if null the current user is used * @return boolean true on success or false on failure * @since 5.0.0 - parameter $owner was added in 8.0.0 */ public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) { return \OC\Share\Share::unshare($itemType, $itemSource, $shareType, $shareWith, $owner); } /** * Unshare an item from all users, groups, and remove all links * @param string $itemType * @param string $itemSource * @return boolean true on success or false on failure * @since 5.0.0 */ public static function unshareAll($itemType, $itemSource) { return \OC\Share\Share::unshareAll($itemType, $itemSource); } /** * Unshare an item shared with the current user * @param string $itemType * @param string $itemOrigin Item target or source * @param boolean $originIsSource true if $itemOrigin is the source, false if $itemOrigin is the target (optional) * @return boolean true on success or false on failure * * Unsharing from self is not allowed for items inside collections * @since 5.0.0 - parameter $originIsSource was added in 8.0.0 */ public static function unshareFromSelf($itemType, $itemOrigin, $originIsSource = false) { return \OC\Share\Share::unshareFromSelf($itemType, $itemOrigin, $originIsSource); } /** * sent status if users got informed by mail about share * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $recipient with whom was the item shared * @param bool $status * @since 6.0.0 - parameter $originIsSource was added in 8.0.0 */ public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) { return \OC\Share\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status); } /** * Set the permissions of an item for a specific user or group * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $shareWith User or group the item is being shared with * @param int $permissions CRUDS permissions * @return boolean true on success or false on failure * @since 5.0.0 */ public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) { return \OC\Share\Share::setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions); } /** * Set expiration date for a share * @param string $itemType * @param string $itemSource * @param string $date expiration date * @param int $shareTime timestamp from when the file was shared * @return boolean * @since 5.0.0 - parameter $shareTime was added in 8.0.0 */ public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) { return \OC\Share\Share::setExpirationDate($itemType, $itemSource, $date, $shareTime); } /** * Set password for a public link share * @param int $shareId * @param string $password * @return boolean * @since 8.1.0 */ public static function setPassword($shareId, $password) { $userSession = \OC::$server->getUserSession(); $connection = \OC::$server->getDatabaseConnection(); $config = \OC::$server->getConfig(); return \OC\Share\Share::setPassword($userSession, $connection, $config, $shareId, $password); } /** * Get the backend class for the specified item type * @param string $itemType * @return Share_Backend * @since 5.0.0 */ public static function getBackend($itemType) { return \OC\Share\Share::getBackend($itemType); } /** * Delete all shares with type SHARE_TYPE_LINK * @since 6.0.0 */ public static function removeAllLinkShares() { return \OC\Share\Share::removeAllLinkShares(); } /** * In case a password protected link is not yet authenticated this function will return false * * @param array $linkItem * @return bool * @since 7.0.0 */ public static function checkPasswordProtectedShare(array $linkItem) { return \OC\Share\Share::checkPasswordProtectedShare($linkItem); } /** * Check if resharing is allowed * * @return boolean true if allowed or false * @since 5.0.0 */ public static function isResharingAllowed() { return \OC\Share\Share::isResharingAllowed(); } } public/IEventSource.php 0000604 00000002775 15247130451 0011117 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) * includes a fallback for older browsers and IE * * use server side events with caution, to many open requests can hang the server * * The event source will initialize the connection to the client when the first data is sent * @since 8.0.0 */ interface IEventSource { /** * send a message to the client * * @param string $type * @param mixed $data * * if only one parameter is given, a typeless message will be send with that parameter as data * @since 8.0.0 */ public function send($type, $data = null); /** * close the connection of the event source * @since 8.0.0 */ public function close(); } public/Config.php 0000604 00000011620 15247130451 0007736 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Config Class * */ /** * Use OCP namespace for all classes that are considered public. * * Classes that use this namespace are for use by apps, and not for use by internal * OC classes */ namespace OCP; /** * This class provides functions to read and write configuration data. * configuration can be on a system, application or user level * @deprecated 8.0.0 use methods of \OCP\IConfig */ class Config { /** * Gets a value from config.php * @param string $key key * @param mixed $default = null default value * @return mixed the value or $default * @deprecated 8.0.0 use method getSystemValue of \OCP\IConfig * * This function gets the value from config.php. If it does not exist, * $default will be returned. */ public static function getSystemValue( $key, $default = null ) { return \OC::$server->getConfig()->getSystemValue( $key, $default ); } /** * Sets a value * @param string $key key * @param mixed $value value * @return bool * @deprecated 8.0.0 use method setSystemValue of \OCP\IConfig * * This function sets the value and writes the config.php. If the file can * not be written, false will be returned. */ public static function setSystemValue( $key, $value ) { try { \OC::$server->getConfig()->setSystemValue( $key, $value ); } catch (\Exception $e) { return false; } return true; } /** * Deletes a value from config.php * @param string $key key * @deprecated 8.0.0 use method deleteSystemValue of \OCP\IConfig * * This function deletes the value from config.php. */ public static function deleteSystemValue( $key ) { \OC::$server->getConfig()->deleteSystemValue( $key ); } /** * Gets the config value * @param string $app app * @param string $key key * @param string $default = null, default value if the key does not exist * @return string the value or $default * @deprecated 8.0.0 use method getAppValue of \OCP\IConfig * * This function gets a value from the appconfig table. If the key does * not exist the default value will be returned */ public static function getAppValue( $app, $key, $default = null ) { return \OC::$server->getConfig()->getAppValue( $app, $key, $default ); } /** * Sets a value in the appconfig * @param string $app app * @param string $key key * @param string $value value * @return boolean true/false * @deprecated 8.0.0 use method setAppValue of \OCP\IConfig * * Sets a value. If the key did not exist before it will be created. */ public static function setAppValue( $app, $key, $value ) { try { \OC::$server->getConfig()->setAppValue( $app, $key, $value ); } catch (\Exception $e) { return false; } return true; } /** * Gets the preference * @param string $user user * @param string $app app * @param string $key key * @param string $default = null, default value if the key does not exist * @return string the value or $default * @deprecated 8.0.0 use method getUserValue of \OCP\IConfig * * This function gets a value from the preferences table. If the key does * not exist the default value will be returned */ public static function getUserValue( $user, $app, $key, $default = null ) { return \OC::$server->getConfig()->getUserValue( $user, $app, $key, $default ); } /** * Sets a value in the preferences * @param string $user user * @param string $app app * @param string $key key * @param string $value value * @return bool * @deprecated 8.0.0 use method setUserValue of \OCP\IConfig * * Adds a value to the preferences. If the key did not exist before, it * will be added automagically. */ public static function setUserValue( $user, $app, $key, $value ) { try { \OC::$server->getConfig()->setUserValue( $user, $app, $key, $value ); } catch (\Exception $e) { return false; } return true; } } public/IAvatarManager.php 0000604 00000002625 15247130451 0011360 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * This class provides avatar functionality * @since 6.0.0 */ interface IAvatarManager { /** * return a user specific instance of \OCP\IAvatar * @see \OCP\IAvatar * @param string $user the ownCloud user id * @return \OCP\IAvatar * @throws \Exception In case the username is potentially dangerous * @throws \OCP\Files\NotFoundException In case there is no user folder yet * @since 6.0.0 */ public function getAvatar($user); } public/L10N/IFactory.php 0000604 00000003421 15247130451 0010723 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\L10N; /** * @since 8.2.0 */ interface IFactory { /** * Get a language instance * * @param string $app * @param string|null $lang * @return \OCP\IL10N * @since 8.2.0 */ public function get($app, $lang = null); /** * Find the best language * * @param string|null $app App id or null for core * @return string language If nothing works it returns 'en' * @since 9.0.0 */ public function findLanguage($app = null); /** * Find all available languages for an app * * @param string|null $app App id or null for core * @return string[] an array of available languages * @since 9.0.0 */ public function findAvailableLanguages($app = null); /** * @param string|null $app App id or null for core * @param string $lang * @return bool * @since 9.0.0 */ public function languageExists($app, $lang); /** * Creates a function from the plural string * * @param string $string * @return string Unique function name * @since 9.0.0 */ public function createPluralFunction($string); } public/Capabilities/ICapability.php 0000604 00000002451 15247130451 0013316 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Capabilities; /** * Minimal interface that has to be implemented for a class to be considered * a capability. * * In an application use: * $this->getContainer()->registerCapability('OCA\MY_APP\Capabilities'); * To register capabilities. * * The class 'OCA\MY_APP\Capabilities' must then implement ICapability * * @since 8.2.0 */ interface ICapability { /** * Function an app uses to return the capabilities * * @return array Array containing the apps capabilities * @since 8.2.0 */ public function getCapabilities(); } public/Settings/ISection.php 0000604 00000003201 15247130451 0012042 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Settings; /** * @deprecated 12 Use IIconSection instead * @since 9.1 */ interface ISection { /** * returns the ID of the section. It is supposed to be a lower case string, * e.g. 'ldap' * * @returns string * @since 9.1 */ public function getID(); /** * returns the translated name as it should be displayed, e.g. 'LDAP / AD * integration'. Use the L10N service to translate it. * * @return string * @since 9.1 */ public function getName(); /** * @return int whether the form should be rather on the top or bottom of * the settings navigation. The sections are arranged in ascending order of * the priority values. It is required to return a value between 0 and 99. * * E.g.: 70 * @since 9.1 */ public function getPriority(); } public/Settings/ISettings.php 0000604 00000003005 15247130451 0012240 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Settings; use OCP\AppFramework\Http\TemplateResponse; /** * @since 9.1 */ interface ISettings { /** * @return TemplateResponse returns the instance with all parameters set, ready to be rendered * @since 9.1 */ public function getForm(); /** * @return string the section ID, e.g. 'sharing' * @since 9.1 */ public function getSection(); /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 * @since 9.1 */ public function getPriority(); } public/Settings/IManager.php 0000604 00000006131 15247130451 0012015 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Settings; /** * @since 9.1 */ interface IManager { /** * @since 9.1.0 */ const KEY_ADMIN_SETTINGS = 'admin'; /** * @since 9.1.0 */ const KEY_ADMIN_SECTION = 'admin-section'; /** * sets up settings according to data specified by an apps info.xml, within * the <settings> element. * * @param array $settings an associative array, allowed keys are as specified * by the KEY_ constant of this interface. The value * must always be a class name, implement either * IAdmin or ISection. I.e. only one section and admin * setting can be configured per app. * @since 9.1.0 */ public function setupSettings(array $settings); /** * attempts to remove an apps section and/or settings entry. A listener is * added centrally making sure that this method is called ones an app was * disabled. * * What this does not help with is when applications change their settings * or section classes during their life time. New entries will be added, * but inactive ones will still reside in the database. * * @param string $appId * @since 9.1.0 */ public function onAppDisabled($appId); /** * The method should check all registered classes whether they are still * instantiable and remove them, if not. This method is called by a * background job once, after one or more apps were updated. * * An app`s info.xml can change during an update and make it unknown whether * a registered class name was changed or not. An old one would just stay * registered. Another case is if an admin takes a radical approach and * simply removes an app from the app folder. These unregular checks will * take care of such situations. * * @since 9.1.0 */ public function checkForOrphanedClassNames(); /** * returns a list of the admin sections * * @return array array of ISection[] where key is the priority * @since 9.1.0 */ public function getAdminSections(); /** * returns a list of the admin settings * * @param string $section the section id for which to load the settings * @return array array of IAdmin[] where key is the priority * @since 9.1.0 */ public function getAdminSettings($section); } public/Settings/IIconSection.php 0000604 00000002164 15247130451 0012662 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Settings; /** * @since 12 */ interface IIconSection extends ISection { /** * returns the relative path to an 16*16 icon describing the section. * e.g. '/core/img/places/files.svg' * * @returns string * @since 12 */ public function getIcon(); } public/IDBConnection.php 0000604 00000016237 15247130451 0011160 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * DBConnection interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; use OCP\DB\QueryBuilder\IQueryBuilder; /** * Interface IDBConnection * * @package OCP * @since 6.0.0 */ interface IDBConnection { /** * Gets the QueryBuilder for the connection. * * @return \OCP\DB\QueryBuilder\IQueryBuilder * @since 8.2.0 */ public function getQueryBuilder(); /** * Used to abstract the ownCloud database access away * @param string $sql the sql query with ? placeholder for params * @param int $limit the maximum number of rows * @param int $offset from which row we want to start * @return \Doctrine\DBAL\Driver\Statement The prepared statement. * @since 6.0.0 */ public function prepare($sql, $limit=null, $offset=null); /** * Executes an, optionally parameterized, SQL query. * * If the query is parameterized, a prepared statement is used. * If an SQLLogger is configured, the execution is logged. * * @param string $query The SQL query to execute. * @param string[] $params The parameters to bind to the query, if any. * @param array $types The types the previous parameters are in. * @return \Doctrine\DBAL\Driver\Statement The executed statement. * @since 8.0.0 */ public function executeQuery($query, array $params = array(), $types = array()); /** * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters * and returns the number of affected rows. * * This method supports PDO binding types as well as DBAL mapping types. * * @param string $query The SQL query. * @param array $params The query parameters. * @param array $types The parameter types. * @return integer The number of affected rows. * @since 8.0.0 */ public function executeUpdate($query, array $params = array(), array $types = array()); /** * Used to get the id of the just inserted element * @param string $table the name of the table where we inserted the item * @return int the id of the inserted element * @since 6.0.0 */ public function lastInsertId($table = null); /** * Insert a row if the matching row does not exists. * * @param string $table The table name (will replace *PREFIX* with the actual prefix) * @param array $input data that should be inserted into the table (column name => value) * @param array|null $compare List of values that should be checked for "if not exists" * If this is null or an empty array, all keys of $input will be compared * Please note: text fields (clob) must not be used in the compare array * @return int number of inserted rows * @throws \Doctrine\DBAL\DBALException * @since 6.0.0 - parameter $compare was added in 8.1.0, return type changed from boolean in 8.1.0 */ public function insertIfNotExist($table, $input, array $compare = null); /** * Insert or update a row value * * @param string $table * @param array $keys (column name => value) * @param array $values (column name => value) * @param array $updatePreconditionValues ensure values match preconditions (column name => value) * @return int number of new rows * @throws \Doctrine\DBAL\DBALException * @throws PreconditionNotMetException * @since 9.0.0 */ public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []); /** * Create an exclusive read+write lock on a table * * Important Note: Due to the nature how locks work on different DBs, it is * only possible to lock one table at a time. You should also NOT start a * transaction while holding a lock. * * @param string $tableName * @since 9.1.0 */ public function lockTable($tableName); /** * Release a previous acquired lock again * * @since 9.1.0 */ public function unlockTable(); /** * Start a transaction * @since 6.0.0 */ public function beginTransaction(); /** * Check if a transaction is active * * @return bool * @since 8.2.0 */ public function inTransaction(); /** * Commit the database changes done during a transaction that is in progress * @since 6.0.0 */ public function commit(); /** * Rollback the database changes done during a transaction that is in progress * @since 6.0.0 */ public function rollBack(); /** * Gets the error code and message as a string for logging * @return string * @since 6.0.0 */ public function getError(); /** * Fetch the SQLSTATE associated with the last database operation. * * @return integer The last error code. * @since 8.0.0 */ public function errorCode(); /** * Fetch extended error information associated with the last database operation. * * @return array The last error information. * @since 8.0.0 */ public function errorInfo(); /** * Establishes the connection with the database. * * @return bool * @since 8.0.0 */ public function connect(); /** * Close the database connection * @since 8.0.0 */ public function close(); /** * Quotes a given input parameter. * * @param mixed $input Parameter to be quoted. * @param int $type Type of the parameter. * @return string The quoted parameter. * @since 8.0.0 */ public function quote($input, $type = IQueryBuilder::PARAM_STR); /** * Gets the DatabasePlatform instance that provides all the metadata about * the platform this driver connects to. * * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform. * @since 8.0.0 */ public function getDatabasePlatform(); /** * Drop a table from the database if it exists * * @param string $table table name without the prefix * @since 8.0.0 */ public function dropTable($table); /** * Check if a table exists * * @param string $table table name without the prefix * @return bool * @since 8.0.0 */ public function tableExists($table); /** * Escape a parameter to be used in a LIKE query * * @param string $param * @return string * @since 9.0.0 */ public function escapeLikeParameter($param); /** * Check whether or not the current database support 4byte wide unicode * * @return bool * @since 11.0.0 */ public function supports4ByteText(); } public/Notification/INotifier.php 0000604 00000002336 15247130451 0013053 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Notification; /** * Interface INotifier * * @package OCP\Notification * @since 9.0.0 */ interface INotifier { /** * @param INotification $notification * @param string $languageCode The code of the language that should be used to prepare the notification * @return INotification * @throws \InvalidArgumentException When the notification was not prepared by a notifier * @since 9.0.0 */ public function prepare(INotification $notification, $languageCode); } public/Notification/INotification.php 0000604 00000012541 15247130451 0013721 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Notification; /** * Interface INotification * * @package OCP\Notification * @since 9.0.0 */ interface INotification { /** * @param string $app * @return $this * @throws \InvalidArgumentException if the app id is invalid * @since 9.0.0 */ public function setApp($app); /** * @return string * @since 9.0.0 */ public function getApp(); /** * @param string $user * @return $this * @throws \InvalidArgumentException if the user id is invalid * @since 9.0.0 */ public function setUser($user); /** * @return string * @since 9.0.0 */ public function getUser(); /** * @param \DateTime $dateTime * @return $this * @throws \InvalidArgumentException if the $dateTime is invalid * @since 9.0.0 */ public function setDateTime(\DateTime $dateTime); /** * @return \DateTime * @since 9.0.0 */ public function getDateTime(); /** * @param string $type * @param string $id * @return $this * @throws \InvalidArgumentException if the object type or id is invalid * @since 9.0.0 */ public function setObject($type, $id); /** * @return string * @since 9.0.0 */ public function getObjectType(); /** * @return string * @since 9.0.0 */ public function getObjectId(); /** * @param string $subject * @param array $parameters * @return $this * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 9.0.0 */ public function setSubject($subject, array $parameters = []); /** * @return string * @since 9.0.0 */ public function getSubject(); /** * @return string[] * @since 9.0.0 */ public function getSubjectParameters(); /** * @param string $subject * @return $this * @throws \InvalidArgumentException if the subject is invalid * @since 9.0.0 */ public function setParsedSubject($subject); /** * @return string * @since 9.0.0 */ public function getParsedSubject(); /** * @param string $subject * @param array $parameters * @return $this * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 11.0.0 */ public function setRichSubject($subject, array $parameters = []); /** * @return string * @since 11.0.0 */ public function getRichSubject(); /** * @return array[] * @since 11.0.0 */ public function getRichSubjectParameters(); /** * @param string $message * @param array $parameters * @return $this * @throws \InvalidArgumentException if the message or parameters are invalid * @since 9.0.0 */ public function setMessage($message, array $parameters = []); /** * @return string * @since 9.0.0 */ public function getMessage(); /** * @return string[] * @since 9.0.0 */ public function getMessageParameters(); /** * @param string $message * @return $this * @throws \InvalidArgumentException if the message is invalid * @since 9.0.0 */ public function setParsedMessage($message); /** * @return string * @since 9.0.0 */ public function getParsedMessage(); /** * @param string $message * @param array $parameters * @return $this * @throws \InvalidArgumentException if the message or parameters are invalid * @since 11.0.0 */ public function setRichMessage($message, array $parameters = []); /** * @return string * @since 11.0.0 */ public function getRichMessage(); /** * @return array[] * @since 11.0.0 */ public function getRichMessageParameters(); /** * @param string $link * @return $this * @throws \InvalidArgumentException if the link is invalid * @since 9.0.0 */ public function setLink($link); /** * @return string * @since 9.0.0 */ public function getLink(); /** * @param string $icon * @return $this * @throws \InvalidArgumentException if the icon is invalid * @since 11.0.0 */ public function setIcon($icon); /** * @return string * @since 11.0.0 */ public function getIcon(); /** * @return IAction * @since 9.0.0 */ public function createAction(); /** * @param IAction $action * @return $this * @throws \InvalidArgumentException if the action is invalid * @since 9.0.0 */ public function addAction(IAction $action); /** * @return IAction[] * @since 9.0.0 */ public function getActions(); /** * @param IAction $action * @return $this * @throws \InvalidArgumentException if the action is invalid * @since 9.0.0 */ public function addParsedAction(IAction $action); /** * @return IAction[] * @since 9.0.0 */ public function getParsedActions(); /** * @return bool * @since 9.0.0 */ public function isValid(); /** * @return bool * @since 9.0.0 */ public function isValidParsed(); } public/Notification/IApp.php 0000604 00000002472 15247130451 0012015 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Notification; /** * Interface IApp * * @package OCP\Notification * @since 9.0.0 */ interface IApp { /** * @param INotification $notification * @throws \InvalidArgumentException When the notification is not valid * @since 9.0.0 */ public function notify(INotification $notification); /** * @param INotification $notification * @since 9.0.0 */ public function markProcessed(INotification $notification); /** * @param INotification $notification * @return int * @since 9.0.0 */ public function getCount(INotification $notification); } public/Notification/IManager.php 0000604 00000003371 15247130451 0012646 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Notification; /** * Interface IManager * * @package OCP\Notification * @since 9.0.0 */ interface IManager extends IApp, INotifier { /** * @param \Closure $service The service must implement IApp, otherwise a * \InvalidArgumentException is thrown later * @since 9.0.0 */ public function registerApp(\Closure $service); /** * @param \Closure $service The service must implement INotifier, otherwise a * \InvalidArgumentException is thrown later * @param \Closure $info An array with the keys 'id' and 'name' containing * the app id and the app name * @since 9.0.0 */ public function registerNotifier(\Closure $service, \Closure $info); /** * @return array App ID => App Name * @since 9.0.0 */ public function listNotifiers(); /** * @return INotification * @since 9.0.0 */ public function createNotification(); /** * @return bool * @since 9.0.0 */ public function hasNotifiers(); } public/Notification/IAction.php 0000604 00000004143 15247130451 0012507 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Notification; /** * Interface IAction * * @package OCP\Notification * @since 9.0.0 */ interface IAction { /** * @param string $label * @return $this * @throws \InvalidArgumentException if the label is invalid * @since 9.0.0 */ public function setLabel($label); /** * @return string * @since 9.0.0 */ public function getLabel(); /** * @param string $label * @return $this * @throws \InvalidArgumentException if the label is invalid * @since 9.0.0 */ public function setParsedLabel($label); /** * @return string * @since 9.0.0 */ public function getParsedLabel(); /** * @param $primary bool * @return $this * @throws \InvalidArgumentException if $primary is invalid * @since 9.0.0 */ public function setPrimary($primary); /** * @return bool * @since 9.0.0 */ public function isPrimary(); /** * @param string $link * @param string $requestType * @return $this * @throws \InvalidArgumentException if the link is invalid * @since 9.0.0 */ public function setLink($link, $requestType); /** * @return string * @since 9.0.0 */ public function getLink(); /** * @return string * @since 9.0.0 */ public function getRequestType(); /** * @return bool * @since 9.0.0 */ public function isValid(); /** * @return bool * @since 9.0.0 */ public function isValidParsed(); } public/SabrePluginEvent.php 0000604 00000003636 15247130451 0011756 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; use OCP\AppFramework\Http; use Sabre\DAV\Server; use Symfony\Component\EventDispatcher\Event; /** * @since 8.2.0 */ class SabrePluginEvent extends Event { /** @var int */ protected $statusCode; /** @var string */ protected $message; /** @var Server */ protected $server; /** * @since 8.2.0 */ public function __construct($server = null) { $this->message = ''; $this->statusCode = Http::STATUS_OK; $this->server = $server; } /** * @param int $statusCode * @return self * @since 8.2.0 */ public function setStatusCode($statusCode) { $this->statusCode = (int) $statusCode; return $this; } /** * @param string $message * @return self * @since 8.2.0 */ public function setMessage($message) { $this->message = (string) $message; return $this; } /** * @return int * @since 8.2.0 */ public function getStatusCode() { return $this->statusCode; } /** * @return string * @since 8.2.0 */ public function getMessage() { return $this->message; } /** * @return null|Server * @since 9.0.0 */ public function getServer() { return $this->server; } } public/Console/ConsoleEvent.php 0000604 00000002770 15247130451 0012545 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Console; use Symfony\Component\EventDispatcher\Event; /** * Class ConsoleEvent * * @package OCP\Console * @since 9.0.0 */ class ConsoleEvent extends Event { const EVENT_RUN = 'OC\Console\Application::run'; /** @var string */ protected $event; /** @var string[] */ protected $arguments; /** * DispatcherEvent constructor. * * @param string $event * @param string[] $arguments * @since 9.0.0 */ public function __construct($event, array $arguments) { $this->event = $event; $this->arguments = $arguments; } /** * @return string * @since 9.0.0 */ public function getEvent() { return $this->event; } /** * @return string[] * @since 9.0.0 */ public function getArguments() { return $this->arguments; } } public/IPreview.php 0000604 00000007467 15247130451 0010301 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Preview interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; use OCP\Files\File; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\NotFoundException; /** * This class provides functions to render and show thumbnails and previews of files * @since 6.0.0 */ interface IPreview { /** * @since 9.2.0 */ const EVENT = self::class . ':' . 'PreviewRequested'; const MODE_FILL = 'fill'; const MODE_COVER = 'cover'; /** * In order to improve lazy loading a closure can be registered which will be * called in case preview providers are actually requested * * $callable has to return an instance of \OCP\Preview\IProvider * * @param string $mimeTypeRegex Regex with the mime types that are supported by this provider * @param \Closure $callable * @return void * @since 8.1.0 */ public function registerProvider($mimeTypeRegex, \Closure $callable); /** * Get all providers * @return array * @since 8.1.0 */ public function getProviders(); /** * Does the manager have any providers * @return bool * @since 8.1.0 */ public function hasProviders(); /** * Return a preview of a file * @param string $file The path to the file where you want a thumbnail from * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image * @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly * @return \OCP\IImage * @since 6.0.0 * @deprecated 11 Use getPreview */ public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false); /** * Returns a preview of a file * * The cache is searched first and if nothing usable was found then a preview is * generated by one of the providers * * @param File $file * @param int $width * @param int $height * @param bool $crop * @param string $mode * @param string $mimeType To force a given mimetype for the file (files_versions needs this) * @return ISimpleFile * @throws NotFoundException * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) * @since 11.0.0 - \InvalidArgumentException was added in 12.0.0 */ public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null); /** * Returns true if the passed mime type is supported * @param string $mimeType * @return boolean * @since 6.0.0 */ public function isMimeSupported($mimeType = '*'); /** * Check if a preview can be generated for a file * * @param \OCP\Files\FileInfo $file * @return bool * @since 8.0.0 */ public function isAvailable(\OCP\Files\FileInfo $file); } public/IContainer.php 0000604 00000006047 15247130451 0010573 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Container interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; use Closure; use OCP\AppFramework\QueryException; /** * Class IContainer * * IContainer is the basic interface to be used for any internal dependency injection mechanism * * @package OCP * @since 6.0.0 */ interface IContainer { /** * If a parameter is not registered in the container try to instantiate it * by using reflection to find out how to build the class * @param string $name the class name to resolve * @return \stdClass * @since 8.2.0 * @throws QueryException if the class could not be found or instantiated */ public function resolve($name); /** * Look up a service for a given name in the container. * * @param string $name * @return mixed * @throws QueryException if the query could not be resolved * @since 6.0.0 */ public function query($name); /** * A value is stored in the container with it's corresponding name * * @param string $name * @param mixed $value * @return void * @since 6.0.0 */ public function registerParameter($name, $value); /** * A service is registered in the container where a closure is passed in which will actually * create the service on demand. * In case the parameter $shared is set to true (the default usage) the once created service will remain in * memory and be reused on subsequent calls. * In case the parameter is false the service will be recreated on every call. * * @param string $name * @param \Closure $closure * @param bool $shared * @return void * @since 6.0.0 */ public function registerService($name, Closure $closure, $shared = true); /** * Shortcut for returning a service from a service under a different key, * e.g. to tell the container to return a class when queried for an * interface * @param string $alias the alias that should be registered * @param string $target the target that should be resolved instead * @since 8.2.0 */ public function registerAlias($alias, $target); } public/ITagManager.php 0000604 00000004373 15247130451 0010657 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Reiter <ockham@raz.or.at> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Tag manager interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Factory class creating instances of \OCP\ITags * * A tag can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or * anything else that is either parsed from a vobject or that the user chooses * to add. * Tag names are not case-sensitive, but will be saved with the case they * are entered in. If a user already has a tag 'family' for a type, and * tries to add a tag named 'Family' it will be silently ignored. * @since 6.0.0 */ interface ITagManager { /** * Create a new \OCP\ITags instance and load tags from db for the current user. * * @see \OCP\ITags * @param string $type The type identifier e.g. 'contact' or 'event'. * @param array $defaultTags An array of default tags to be used if none are stored. * @param boolean $includeShared Whether to include tags for items shared with this user by others. * @param string $userId user for which to retrieve the tags, defaults to the currently * logged in user * @return \OCP\ITags * @since 6.0.0 - parameter $includeShared and $userId were added in 8.0.0 */ public function load($type, $defaultTags = array(), $includeShared = false, $userId = null); } public/INavigationManager.php 0000604 00000003476 15247130451 0012246 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Navigation manager interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Manages the ownCloud navigation * @since 6.0.0 */ interface INavigationManager { /** * Creates a new navigation entry * * @param array|\Closure $entry Array containing: id, name, order, icon and href key * The use of a closure is preferred, because it will avoid * loading the routing of your app, unless required. * @return void * @since 6.0.0 */ public function add($entry); /** * Sets the current navigation entry of the currently running app * @param string $appId id of the app entry to activate (from added $entry) * @return void * @since 6.0.0 */ public function setActiveEntry($appId); } public/Lock/LockedException.php 0000604 00000002707 15247130451 0012507 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Lock; /** * Class LockedException * * @package OCP\Lock * @since 8.1.0 */ class LockedException extends \Exception { /** * Locked path * * @var string */ private $path; /** * LockedException constructor. * * @param string $path locked path * @param \Exception $previous previous exception for cascading * * @since 8.1.0 */ public function __construct($path, \Exception $previous = null) { parent::__construct('"' . $path . '" is locked', 0, $previous); $this->path = $path; } /** * @return string * @since 8.1.0 */ public function getPath() { return $this->path; } } public/Lock/ILockingProvider.php 0000604 00000003600 15247130451 0012632 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Lock; /** * Interface ILockingProvider * * @package OCP\Lock * @since 8.1.0 */ interface ILockingProvider { /** * @since 8.1.0 */ const LOCK_SHARED = 1; /** * @since 8.1.0 */ const LOCK_EXCLUSIVE = 2; /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @return bool * @since 8.1.0 */ public function isLocked($path, $type); /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException * @since 8.1.0 */ public function acquireLock($path, $type); /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @since 8.1.0 */ public function releaseLock($path, $type); /** * Change the type of an existing lock * * @param string $path * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException * @since 8.1.0 */ public function changeLock($path, $targetType); /** * release all lock acquired by this instance * @since 8.1.0 */ public function releaseAll(); } public/Diagnostics/IQuery.php 0000604 00000002551 15247130451 0012221 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Diagnostics; /** * Interface IQuery * * @package OCP\Diagnostics * @since 8.0.0 */ interface IQuery { /** * @return string * @since 8.0.0 */ public function getSql(); /** * @return array * @since 8.0.0 */ public function getParams(); /** * @return float * @since 8.0.0 */ public function getDuration(); /** * @return float * @since 11.0.0 */ public function getStartTime(); /** * @return array * @since 11.0.0 */ public function getStacktrace(); /** * @return array * @since 12.0.0 */ public function getStart(); } public/Diagnostics/IEventLogger.php 0000604 00000004623 15247130451 0013337 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Piotr Mrowczynski <piotr@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Diagnostics; /** * Interface IEventLogger * * @package OCP\Diagnostics * @since 8.0.0 */ interface IEventLogger { /** * Mark the start of an event setting its ID $id and providing event description $description. * * @param string $id * @param string $description * @since 8.0.0 */ public function start($id, $description); /** * Mark the end of an event with specific ID $id, marked by start() method. * Ending event should store \OCP\Diagnostics\IEvent to * be returned with getEvents() method. * * @param string $id * @since 8.0.0 */ public function end($id); /** * Mark the start and the end of an event with specific ID $id and description $description, * explicitly marking start and end of the event, represented by $start and $end timestamps. * Logging event should store \OCP\Diagnostics\IEvent to * be returned with getEvents() method. * * @param string $id * @param string $description * @param float $start * @param float $end * @since 8.0.0 */ public function log($id, $description, $start, $end); /** * This method should return all \OCP\Diagnostics\IEvent objects stored using * start()/end() or log() methods * * @return \OCP\Diagnostics\IEvent[] * @since 8.0.0 */ public function getEvents(); /** * Activate the module for the duration of the request. Deactivated module * does not create and store \OCP\Diagnostics\IEvent objects. * Only activated module should create and store objects to be * returned with getEvents() call. * * @since 12.0.0 */ public function activate(); } public/Diagnostics/IEvent.php 0000604 00000002426 15247130451 0012176 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Diagnostics; /** * Interface IEvent * * @package OCP\Diagnostics * @since 8.0.0 */ interface IEvent { /** * @return string * @since 8.0.0 */ public function getId(); /** * @return string * @since 8.0.0 */ public function getDescription(); /** * @return float * @since 8.0.0 */ public function getStart(); /** * @return float * @since 8.0.0 */ public function getEnd(); /** * @return float * @since 8.0.0 */ public function getDuration(); } public/Diagnostics/IQueryLogger.php 0000604 00000004214 15247130451 0013357 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Piotr Mrowczynski <piotr@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Diagnostics; use Doctrine\DBAL\Logging\SQLLogger; /** * Interface IQueryLogger * * @package OCP\Diagnostics * @since 8.0.0 */ interface IQueryLogger extends SQLLogger { /** * Mark the start of a query providing query SQL statement, its parameters and types. * This method should be called as close to the DB as possible and after * query is finished finalized with stopQuery() method. * * @param string $sql * @param array $params * @param array $types * @since 8.0.0 */ public function startQuery($sql, array $params = null, array $types = null); /** * Mark the end of the current active query. Ending query should store \OCP\Diagnostics\IQuery to * be returned with getQueries() method. * * @return mixed * @since 8.0.0 */ public function stopQuery(); /** * This method should return all \OCP\Diagnostics\IQuery objects stored using * startQuery()/stopQuery() methods. * * @return \OCP\Diagnostics\IQuery[] * @since 8.0.0 */ public function getQueries(); /** * Activate the module for the duration of the request. Deactivated module * does not create and store \OCP\Diagnostics\IQuery objects. * Only activated module should create and store objects to be * returned with getQueries() call. * * @since 12.0.0 */ public function activate(); } public/WorkflowEngine/IManager.php 0000604 00000002447 15247130451 0013163 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\WorkflowEngine; use OCP\Files\Storage\IStorage; /** * Interface IManager * * @package OCP\WorkflowEngine * @since 9.1 */ interface IManager { /** * @param IStorage $storage * @param string $path * @since 9.1 */ public function setFileInfo(IStorage $storage, $path); /** * @param string $class * @param bool $returnFirstMatchingOperationOnly * @return array * @since 9.1 */ public function getMatchingOperations($class, $returnFirstMatchingOperationOnly = true); } public/WorkflowEngine/IOperation.php 0000604 00000002212 15247130451 0013537 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\WorkflowEngine; /** * Interface IOperation * * @package OCP\WorkflowEngine * @since 9.1 */ interface IOperation { /** * @param string $name * @param array[] $checks * @param string $operation * @throws \UnexpectedValueException * @since 9.1 */ public function validateOperation($name, array $checks, $operation); } public/WorkflowEngine/ICheck.php 0000604 00000002614 15247130451 0012622 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\WorkflowEngine; use OCP\Files\Storage\IStorage; /** * Interface ICheck * * @package OCP\WorkflowEngine * @since 9.1 */ interface ICheck { /** * @param IStorage $storage * @param string $path * @since 9.1 */ public function setFileInfo(IStorage $storage, $path); /** * @param string $operator * @param string $value * @return bool * @since 9.1 */ public function executeCheck($operator, $value); /** * @param string $operator * @param string $value * @throws \UnexpectedValueException * @since 9.1 */ public function validateCheck($operator, $value); } public/App.php 0000604 00000010737 15247130451 0007261 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * App Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides functions to manage apps in ownCloud * @since 4.0.0 */ class App { /** * Adds an entry to the navigation * * This function adds a new entry to the navigation visible to users. $data * is an associative array. * The following keys are required: * - id: unique id for this entry ('addressbook_index') * - href: link to the page * - name: Human readable name ('Addressbook') * * The following keys are optional: * - icon: path to the icon of the app * - order: integer, that influences the position of your application in * the navigation. Lower values come first. * * @param array $data containing the data * @return boolean * * @deprecated 8.1.0 Use \OC::$server->getNavigationManager()->add() instead to * register a closure, this helps to speed up all requests against ownCloud * @since 4.0.0 */ public static function addNavigationEntry($data) { \OC::$server->getNavigationManager()->add($data); return true; } /** * Marks a navigation entry as active * @param string $id id of the entry * @return boolean * * This function sets a navigation entry as active and removes the 'active' * property from all other entries. The templates can use this for * highlighting the current position of the user. * * @deprecated 8.1.0 Use \OC::$server->getNavigationManager()->setActiveEntry() instead * @since 4.0.0 */ public static function setActiveNavigationEntry( $id ) { \OC::$server->getNavigationManager()->setActiveEntry($id); return true; } /** * Register a Configuration Screen that should appear in the personal settings section. * @param string $app appid * @param string $page page to be included * @return void * @since 4.0.0 */ public static function registerPersonal( $app, $page ) { \OC_App::registerPersonal( $app, $page ); } /** * Register a Configuration Screen that should appear in the Admin section. * @param string $app string appid * @param string $page string page to be included * @return void * @since 4.0.0 */ public static function registerAdmin( $app, $page ) { \OC_App::registerAdmin( $app, $page ); } /** * Read app metadata from the info.xml file * @param string $app id of the app or the path of the info.xml file * @param boolean $path (optional) * @return array|null * @since 4.0.0 */ public static function getAppInfo( $app, $path=false ) { return \OC_App::getAppInfo( $app, $path); } /** * checks whether or not an app is enabled * @param string $app * @return boolean * * This function checks whether or not an app is enabled. * @since 4.0.0 */ public static function isEnabled( $app ) { return \OC_App::isEnabled( $app ); } /** * Check if the app is enabled, redirects to home if not * @param string $app * @return void * @since 4.0.0 * @deprecated 9.0.0 ownCloud core will handle disabled apps and redirects to valid URLs */ public static function checkAppEnabled( $app ) { } /** * Get the last version of the app from appinfo/info.xml * @param string $app * @return string * @since 4.0.0 */ public static function getAppVersion( $app ) { return \OC_App::getAppVersion( $app ); } } public/ICache.php 0000604 00000004177 15247130451 0007656 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Cache interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This interface defines method for accessing the file based user cache. * @since 6.0.0 */ interface ICache { /** * Get a value from the user cache * @param string $key * @return mixed * @since 6.0.0 */ public function get($key); /** * Set a value in the user cache * @param string $key * @param mixed $value * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 * @return bool * @since 6.0.0 */ public function set($key, $value, $ttl = 0); /** * Check if a value is set in the user cache * @param string $key * @return bool * @since 6.0.0 * @deprecated 9.1.0 Directly read from GET to prevent race conditions */ public function hasKey($key); /** * Remove an item from the user cache * @param string $key * @return bool * @since 6.0.0 */ public function remove($key); /** * Clear the user cache of all entries starting with a prefix * @param string $prefix (optional) * @return bool * @since 6.0.0 */ public function clear($prefix = ''); } public/IDateTimeFormatter.php 0000604 00000012732 15247130451 0012227 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface IDateTimeFormatter * * @package OCP * @since 8.0.0 */ interface IDateTimeFormatter { /** * Formats the date of the given timestamp * * @param int|\DateTime $timestamp * @param string $format Either 'full', 'long', 'medium' or 'short' * full: e.g. 'EEEE, MMMM d, y' => 'Wednesday, August 20, 2014' * long: e.g. 'MMMM d, y' => 'August 20, 2014' * medium: e.g. 'MMM d, y' => 'Aug 20, 2014' * short: e.g. 'M/d/yy' => '8/20/14' * The exact format is dependent on the language * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted date string * @since 8.0.0 */ public function formatDate($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null); /** * Formats the date of the given timestamp * * @param int|\DateTime $timestamp * @param string $format Either 'full', 'long', 'medium' or 'short' * full: e.g. 'EEEE, MMMM d, y' => 'Wednesday, August 20, 2014' * long: e.g. 'MMMM d, y' => 'August 20, 2014' * medium: e.g. 'MMM d, y' => 'Aug 20, 2014' * short: e.g. 'M/d/yy' => '8/20/14' * The exact format is dependent on the language * Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted relative date string * @since 8.0.0 */ public function formatDateRelativeDay($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null); /** * Gives the relative date of the timestamp * Only works for past dates * * @param int|\DateTime $timestamp * @param int|\DateTime $baseTimestamp Timestamp to compare $timestamp against, defaults to current time * @return string Dates returned are: * < 1 month => Today, Yesterday, n days ago * < 13 month => last month, n months ago * >= 13 month => last year, n years ago * @param \OCP\IL10N $l The locale to use * @return string Formatted date span * @since 8.0.0 */ public function formatDateSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null); /** * Formats the time of the given timestamp * * @param int|\DateTime $timestamp * @param string $format Either 'full', 'long', 'medium' or 'short' * full: e.g. 'h:mm:ss a zzzz' => '11:42:13 AM GMT+0:00' * long: e.g. 'h:mm:ss a z' => '11:42:13 AM GMT' * medium: e.g. 'h:mm:ss a' => '11:42:13 AM' * short: e.g. 'h:mm a' => '11:42 AM' * The exact format is dependent on the language * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted time string * @since 8.0.0 */ public function formatTime($timestamp, $format = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null); /** * Gives the relative past time of the timestamp * * @param int|\DateTime $timestamp * @param int|\DateTime $baseTimestamp Timestamp to compare $timestamp against, defaults to current time * @return string Dates returned are: * < 60 sec => seconds ago * < 1 hour => n minutes ago * < 1 day => n hours ago * < 1 month => Yesterday, n days ago * < 13 month => last month, n months ago * >= 13 month => last year, n years ago * @param \OCP\IL10N $l The locale to use * @return string Formatted time span * @since 8.0.0 */ public function formatTimeSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null); /** * Formats the date and time of the given timestamp * * @param int|\DateTime $timestamp * @param string $formatDate See formatDate() for description * @param string $formatTime See formatTime() for description * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted date and time string * @since 8.0.0 */ public function formatDateTime($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null); /** * Formats the date and time of the given timestamp * * @param int|\DateTime $timestamp * @param string $formatDate See formatDate() for description * Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable * @param string $formatTime See formatTime() for description * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted relative date and time string * @since 8.0.0 */ public function formatDateTimeRelativeDay($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null); } public/IUserSession.php 0000604 00000004052 15247130451 0011125 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * User session interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * User session * @since 6.0.0 */ interface IUserSession { /** * Do a user login * @param string $user the username * @param string $password the password * @return bool true if successful * @since 6.0.0 */ public function login($user, $password); /** * Logs the user out including all the session data * Logout, destroys session * @return void * @since 6.0.0 */ public function logout(); /** * set the currently active user * * @param \OCP\IUser|null $user * @since 8.0.0 */ public function setUser($user); /** * get the current active user * * @return \OCP\IUser|null Current user, otherwise null * @since 8.0.0 */ public function getUser(); /** * Checks whether the user is logged in * * @return bool if logged in * @since 8.0.0 */ public function isLoggedIn(); } public/Migration/IRepairStep.php 0000604 00000002262 15247130451 0012653 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Migration; /** * Repair step * @since 9.1.0 */ interface IRepairStep { /** * Returns the step's name * * @return string * @since 9.1.0 */ public function getName(); /** * Run repair step. * Must throw exception on error. * * @param IOutput $output * @throws \Exception in case of failure * @since 9.1.0 */ public function run(IOutput $output); } public/Migration/IOutput.php 0000604 00000002612 15247130451 0012074 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Migration; /** * Interface IOutput * * @package OCP\Migration * @since 9.1.0 */ interface IOutput { /** * @param string $message * @since 9.1.0 */ public function info($message); /** * @param string $message * @since 9.1.0 */ public function warning($message); /** * @param int $max * @since 9.1.0 */ public function startProgress($max = 0); /** * @param int $step * @param string $description * @since 9.1.0 */ public function advance($step = 1, $description = ''); /** * @param int $max * @since 9.1.0 */ public function finishProgress(); } public/GlobalScale/IConfig.php 0000604 00000002422 15247130451 0012217 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\GlobalScale; /** * Interface IConfig * * Configuration of the global scale architecture * * @package OCP\GlobalScale * @since 12.0.1 */ interface IConfig { /** * check if global scale is enabled * * @since 12.0.1 * @return bool */ public function isGlobalScaleEnabled(); /** * check if federation should only be used internally in a global scale setup * * @since 12.0.1 * @return bool */ public function onlyInternalFederation(); } public/IURLGenerator.php 0000604 00000005551 15247130451 0011161 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * URL generator interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Class to generate URLs * @since 6.0.0 */ interface IURLGenerator { /** * Returns the URL for a route * @param string $routeName the name of the route * @param array $arguments an array with arguments which will be filled into the url * @return string the url * @since 6.0.0 */ public function linkToRoute($routeName, $arguments = array()); /** * Returns the absolute URL for a route * @param string $routeName the name of the route * @param array $arguments an array with arguments which will be filled into the url * @return string the absolute url * @since 8.0.0 */ public function linkToRouteAbsolute($routeName, $arguments = array()); /** * Returns an URL for an image or file * @param string $appName the name of the app * @param string $file the name of the file * @param array $args array with param=>value, will be appended to the returned url * The value of $args will be urlencoded * @return string the url * @since 6.0.0 */ public function linkTo($appName, $file, $args = array()); /** * Returns the link to an image, like linkTo but only with prepending img/ * @param string $appName the name of the app * @param string $file the name of the file * @return string the url * @since 6.0.0 */ public function imagePath($appName, $file); /** * Makes an URL absolute * @param string $url the url in the ownCloud host * @return string the absolute version of the url * @since 6.0.0 */ public function getAbsoluteURL($url); /** * @param string $key * @return string url to the online documentation * @since 8.0.0 */ public function linkToDocs($key); /** * @return string base url of the current request * @since 13.0.0 */ public function getBaseUrl(); } public/Security/ISecureRandom.php 0000604 00000006255 15247130451 0013050 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Security; /** * Class SecureRandom provides a wrapper around the random_int function to generate * secure random strings. For PHP 7 the native CSPRNG is used, older versions do * use a fallback. * * Usage: * \OC::$server->getSecureRandom()->generate(10); * * @package OCP\Security * @since 8.0.0 */ interface ISecureRandom { /** * Flags for characters that can be used for <code>generate($length, $characters)</code> */ const CHAR_UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const CHAR_LOWER = 'abcdefghijklmnopqrstuvwxyz'; const CHAR_DIGITS = '0123456789'; const CHAR_SYMBOLS = '!\"#$%&\\\'()* +,-./:;<=>?@[\]^_`{|}~'; /** * Characters that can be used for <code>generate($length, $characters)</code>, to * generate human readable random strings. Lower- and upper-case characters and digits * are included. Characters which are ambiguous are excluded, such as I, l, and 1 and so on. */ const CHAR_HUMAN_READABLE = "abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789"; /** * Convenience method to get a low strength random number generator. * * Low Strength should be used anywhere that random strings are needed * in a non-cryptographical setting. They are not strong enough to be * used as keys or salts. They are however useful for one-time use tokens. * * @return $this * @since 8.0.0 * @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int() */ public function getLowStrengthGenerator(); /** * Convenience method to get a medium strength random number generator. * * Medium Strength should be used for most needs of a cryptographic nature. * They are strong enough to be used as keys and salts. However, they do * take some time and resources to generate, so they should not be over-used * * @return $this * @since 8.0.0 * @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int() */ public function getMediumStrengthGenerator(); /** * Generate a random string of specified length. * @param int $length The length of the generated string * @param string $characters An optional list of characters to use if no character list is * specified all valid base64 characters are used. * @return string * @since 8.0.0 */ public function generate($length, $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'); } public/Security/IHasher.php 0000604 00000004427 15247130451 0011672 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Security; /** * Class Hasher provides some basic hashing functions. Furthermore, it supports legacy hashes * used by previous versions of ownCloud and helps migrating those hashes to newer ones. * * The hashes generated by this class are prefixed (version|hash) with a version parameter to allow possible * updates in the future. * Possible versions: * - 1 (Initial version) * * Usage: * // Hashing a message * $hash = \OC::$server->getHasher()->hash('MessageToHash'); * // Verifying a message - $newHash will contain the newly calculated hash * $newHash = null; * var_dump(\OC::$server->getHasher()->verify('a', '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', $newHash)); * var_dump($newHash); * * @package OCP\Security * @since 8.0.0 */ interface IHasher { /** * Hashes a message using PHP's `password_hash` functionality. * Please note that the size of the returned string is not guaranteed * and can be up to 255 characters. * * @param string $message Message to generate hash from * @return string Hash of the message with appended version parameter * @since 8.0.0 */ public function hash($message); /** * @param string $message Message to verify * @param string $hash Assumed hash of the message * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one. * @return bool Whether $hash is a valid hash of $message * @since 8.0.0 */ public function verify($message, $hash, &$newHash = null); } public/Security/StringUtils.php 0000604 00000002742 15247130451 0012634 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Security; /** * Class StringUtils * * @package OCP\Security * @since 8.0.0 */ class StringUtils { /** * Compares whether two strings are equal. To prevent guessing of the string * length this is done by comparing two hashes against each other and afterwards * a comparison of the real string to prevent against the unlikely chance of * collisions. * @param string $expected The expected value * @param string $input The input to compare against * @return bool True if the two strings are equal, otherwise false. * @since 8.0.0 * @deprecated 9.0.0 Use hash_equals */ public static function equals($expected, $input) { return hash_equals($expected, $input); } } public/Security/IContentSecurityPolicyManager.php 0000604 00000003535 15247130451 0016274 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Security; use OCP\AppFramework\Http\EmptyContentSecurityPolicy; /** * Used for Content Security Policy manipulations * * @package OCP\Security * @since 9.0.0 */ interface IContentSecurityPolicyManager { /** * Allows to inject something into the default content policy. This is for * example useful when you're injecting Javascript code into a view belonging * to another controller and cannot modify its Content-Security-Policy itself. * Note that the adjustment is only applied to applications that use AppFramework * controllers. * * To use this from your `app.php` use `\OC::$server->getContentSecurityPolicyManager()->addDefaultPolicy($policy)`, * $policy has to be of type `\OCP\AppFramework\Http\ContentSecurityPolicy`. * * WARNING: Using this API incorrectly may make the instance more insecure. * Do think twice before adding whitelisting resources. Please do also note * that it is not possible to use the `disallowXYZ` functions. * * @param EmptyContentSecurityPolicy $policy * @since 9.0.0 */ public function addDefaultPolicy(EmptyContentSecurityPolicy $policy); } public/Security/ICrypto.php 0000604 00000004315 15247130451 0011734 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Security; /** * Class Crypto provides a high-level encryption layer using AES-CBC. If no key has been provided * it will use the secret defined in config.php as key. Additionally the message will be HMAC'd. * * Usage: * $encryptWithDefaultPassword = \OC::$server->getCrypto()->encrypt('EncryptedText'); * $encryptWithCustomPassword = \OC::$server->getCrypto()->encrypt('EncryptedText', 'password'); * * @package OCP\Security * @since 8.0.0 */ interface ICrypto { /** * @param string $message The message to authenticate * @param string $password Password to use (defaults to `secret` in config.php) * @return string Calculated HMAC * @since 8.0.0 */ public function calculateHMAC($message, $password = ''); /** * Encrypts a value and adds an HMAC (Encrypt-Then-MAC) * @param string $plaintext * @param string $password Password to encrypt, if not specified the secret from config.php will be taken * @return string Authenticated ciphertext * @since 8.0.0 */ public function encrypt($plaintext, $password = ''); /** * Decrypts a value and verifies the HMAC (Encrypt-Then-Mac) * @param string $authenticatedCiphertext * @param string $password Password to encrypt, if not specified the secret from config.php will be taken * @return string plaintext * @throws \Exception If the HMAC does not match * @since 8.0.0 */ public function decrypt($authenticatedCiphertext, $password = ''); } public/Security/ICredentialsManager.php 0000604 00000003407 15247130451 0014205 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Security; /** * Store and retrieve credentials for external services * * @package OCP\Security * @since 8.2.0 */ interface ICredentialsManager { /** * Store a set of credentials * * @param string|null $userId Null for system-wide credentials * @param string $identifier * @param mixed $credentials * @since 8.2.0 */ public function store($userId, $identifier, $credentials); /** * Retrieve a set of credentials * * @param string|null $userId Null for system-wide credentials * @param string $identifier * @return mixed * @since 8.2.0 */ public function retrieve($userId, $identifier); /** * Delete a set of credentials * * @param string|null $userId Null for system-wide credentials * @param string $identifier * @return int rows removed * @since 8.2.0 */ public function delete($userId, $identifier); /** * Erase all credentials stored for a user * * @param string $userId * @return int rows removed * @since 8.2.0 */ public function erase($userId); } public/BackgroundJob.php 0000604 00000006145 15247130451 0011251 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Felix Moeller <mail@felixmoeller.de> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for background jobs. */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides functions to register backgroundjobs in ownCloud * * To create a new backgroundjob create a new class that inherits from either \OC\BackgroundJob\Job, * \OC\BackgroundJob\QueuedJob or \OC\BackgroundJob\TimedJob and register it using * \OCP\BackgroundJob->registerJob($job, $argument), $argument will be passed to the run() function * of the job when the job is executed. * * A regular Job will be executed every time cron.php is run, a QueuedJob will only run once and a TimedJob * will only run at a specific interval which is to be specified in the constructor of the job by calling * $this->setInterval($interval) with $interval in seconds. * @since 4.5.0 */ class BackgroundJob { /** * get the execution type of background jobs * * @return string * * This method returns the type how background jobs are executed. If the user * did not select something, the type is ajax. * @since 5.0.0 */ public static function getExecutionType() { return \OC::$server->getConfig()->getAppValue('core', 'backgroundjobs_mode', 'ajax'); } /** * sets the background jobs execution type * * @param string $type execution type * @return false|null * * This method sets the execution type of the background jobs. Possible types * are "none", "ajax", "webcron", "cron" * @since 5.0.0 */ public static function setExecutionType($type) { if( !in_array( $type, array('none', 'ajax', 'webcron', 'cron'))) { return false; } \OC::$server->getConfig()->setAppValue('core', 'backgroundjobs_mode', $type); } /** * @param string $job * @param mixed $argument * @deprecated 8.1.0 Use \OC::$server->getJobList()->add() instead * @since 6.0.0 */ public static function registerJob($job, $argument = null) { $jobList = \OC::$server->getJobList(); $jobList->add($job, $argument); } } public/ITags.php 0000604 00000013444 15247130451 0007546 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Reiter <ockham@raz.or.at> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Tags interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; // FIXME: Where should I put this? Or should it be implemented as a Listener? \OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Tags', 'post_deleteUser'); /** * Class for easily tagging objects by their id * * A tag can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or * anything else that is either parsed from a vobject or that the user chooses * to add. * Tag names are not case-sensitive, but will be saved with the case they * are entered in. If a user already has a tag 'family' for a type, and * tries to add a tag named 'Family' it will be silently ignored. * @since 6.0.0 */ interface ITags { /** * Check if any tags are saved for this type and user. * * @return boolean * @since 6.0.0 */ public function isEmpty(); /** * Returns an array mapping a given tag's properties to its values: * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype'] * * @param string $id The ID of the tag that is going to be mapped * @return array|false * @since 8.0.0 */ public function getTag($id); /** * Get the tags for a specific user. * * This returns an array with id/name maps: * [ * ['id' => 0, 'name' = 'First tag'], * ['id' => 1, 'name' = 'Second tag'], * ] * * @return array * @since 6.0.0 */ public function getTags(); /** * Get a list of tags for the given item ids. * * This returns an array with object id / tag names: * [ * 1 => array('First tag', 'Second tag'), * 2 => array('Second tag'), * 3 => array('Second tag', 'Third tag'), * ] * * @param array $objIds item ids * @return array|boolean with object id as key and an array * of tag names as value or false if an error occurred * @since 8.0.0 */ public function getTagsForObjects(array $objIds); /** * Get a list of items tagged with $tag. * * Throws an exception if the tag could not be found. * * @param string|integer $tag Tag id or name. * @return array|false An array of object ids or false on error. * @since 6.0.0 */ public function getIdsForTag($tag); /** * Checks whether a tag is already saved. * * @param string $name The name to check for. * @return bool * @since 6.0.0 */ public function hasTag($name); /** * Checks whether a tag is saved for the given user, * disregarding the ones shared with him or her. * * @param string $name The tag name to check for. * @param string $user The user whose tags are to be checked. * @return bool * @since 8.0.0 */ public function userHasTag($name, $user); /** * Add a new tag. * * @param string $name A string with a name of the tag * @return int|false the id of the added tag or false if it already exists. * @since 6.0.0 */ public function add($name); /** * Rename tag. * * @param string|integer $from The name or ID of the existing tag * @param string $to The new name of the tag. * @return bool * @since 6.0.0 */ public function rename($from, $to); /** * Add a list of new tags. * * @param string[] $names A string with a name or an array of strings containing * the name(s) of the to add. * @param bool $sync When true, save the tags * @param int|null $id int Optional object id to add to this|these tag(s) * @return bool Returns false on error. * @since 6.0.0 */ public function addMultiple($names, $sync=false, $id = null); /** * Delete tag/object relations from the db * * @param array $ids The ids of the objects * @return boolean Returns false on error. * @since 6.0.0 */ public function purgeObjects(array $ids); /** * Get favorites for an object type * * @return array|false An array of object ids. * @since 6.0.0 */ public function getFavorites(); /** * Add an object to favorites * * @param int $objid The id of the object * @return boolean * @since 6.0.0 */ public function addToFavorites($objid); /** * Remove an object from favorites * * @param int $objid The id of the object * @return boolean * @since 6.0.0 */ public function removeFromFavorites($objid); /** * Creates a tag/object relation. * * @param int $objid The id of the object * @param string $tag The id or name of the tag * @return boolean Returns false on database error. * @since 6.0.0 */ public function tagAs($objid, $tag); /** * Delete single tag/object relation from the db * * @param int $objid The id of the object * @param string $tag The id or name of the tag * @return boolean * @since 6.0.0 */ public function unTag($objid, $tag); /** * Delete tags from the database * * @param string[]|integer[] $names An array of tags (names or IDs) to delete * @return bool Returns false on error * @since 6.0.0 */ public function delete($names); } public/UserInterface.php 0000604 00000005250 15247130451 0011272 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * User Interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * TODO actually this is a IUserBackend * * @package OCP * @since 4.5.0 */ interface UserInterface { /** * Check if backend implements actions * @param int $actions bitwise-or'ed actions * @return boolean * * Returns the supported actions as int to be * compared with \OC_User_Backend::CREATE_USER etc. * @since 4.5.0 */ public function implementsActions($actions); /** * delete a user * @param string $uid The username of the user to delete * @return bool * @since 4.5.0 */ public function deleteUser($uid); /** * Get a list of all users * * @param string $search * @param null|int $limit * @param null|int $offset * @return string[] an array of all uids * @since 4.5.0 */ public function getUsers($search = '', $limit = null, $offset = null); /** * check if a user exists * @param string $uid the username * @return boolean * @since 4.5.0 */ public function userExists($uid); /** * get display name of the user * @param string $uid user ID of the user * @return string display name * @since 4.5.0 */ public function getDisplayName($uid); /** * Get a list of all display names and user ids. * * @param string $search * @param string|null $limit * @param string|null $offset * @return array an array of all displayNames (value) and the corresponding uids (key) * @since 4.5.0 */ public function getDisplayNames($search = '', $limit = null, $offset = null); /** * Check if a user list is available or not * @return boolean if users can be listed or not * @since 4.5.0 */ public function hasUserListings(); } public/Mail/IMailer.php 0000604 00000005250 15247130451 0010737 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Mail; use OC\Mail\Message; /** * Class IMailer provides some basic functions to create a mail message that can be used in combination with * \OC\Mail\Message. * * Example usage: * * $mailer = \OC::$server->getMailer(); * $message = $mailer->createMessage(); * $message->setSubject('Your Subject'); * $message->setFrom(['cloud@domain.org' => 'ownCloud Notifier']); * $message->setTo(['recipient@domain.org' => 'Recipient']); * $message->setPlainBody('The message text'); * $message->setHtmlBody('The <strong>message</strong> text'); * $mailer->send($message); * * This message can then be passed to send() of \OC\Mail\Mailer * * @package OCP\Mail * @since 8.1.0 */ interface IMailer { /** * Creates a new message object that can be passed to send() * * @return Message * @since 8.1.0 */ public function createMessage(); /** * Creates a new email template object * * @param string $emailId * @param array $data * @return IEMailTemplate * @since 12.0.0 Parameters added in 12.0.3 */ public function createEMailTemplate($emailId, array $data = []); /** * Send the specified message. Also sets the from address to the value defined in config.php * if no-one has been passed. * * @param Message $message Message to send * @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and * therefore should be considered * @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address * has been supplied.) * @since 8.1.0 */ public function send(Message $message); /** * Checks if an e-mail address is valid * * @param string $email Email address to be validated * @return bool True if the mail address is valid, false otherwise * @since 8.1.0 */ public function validateMailAddress($email); } public/Mail/IEMailTemplate.php 0000604 00000010704 15247130451 0012211 0 ustar 00 <?php /** * @copyright 2017, Morris Jobke <hey@morrisjobke.de> * * @author Morris Jobke <hey@morrisjobke.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Mail; /** * Interface IEMailTemplate * * Interface to a class that allows to build HTML emails * * Example: * * <?php * * $emailTemplate = new EMailTemplate($this->defaults, $this->urlGenerator, $this->l10n); * * $emailTemplate->addHeader(); * $emailTemplate->addHeading('Welcome aboard'); * $emailTemplate->addBodyText('You have now an Nextcloud account, you can add, protect, and share your data.'); * * $emailTemplate->addBodyButtonGroup( * 'Set your password', 'https://example.org/resetPassword/q1234567890qwertz', * 'Install Client', 'https://nextcloud.com/install/#install-clients' * ); * * $emailTemplate->addFooter('Optional footer text'); * * $htmlContent = $emailTemplate->renderHtml(); * $plainContent = $emailTemplate->renderText(); * * @since 12.0.0 */ interface IEMailTemplate { /** * Adds a header to the email * * @since 12.0.0 */ public function addHeader(); /** * Adds a heading to the email * * @param string $title * @param string $plainTitle|bool Title that is used in the plain text email * if empty the $title is used, if false none will be used * * @since 12.0.0 */ public function addHeading($title, $plainTitle = ''); /** * Adds a paragraph to the body of the email * * @param string $text * @param string|bool $plainText Text that is used in the plain text email * if empty the $text is used, if false none will be used * * @since 12.0.0 */ public function addBodyText($text, $plainText = ''); /** * Adds a list item to the body of the email * * @param string $text * @param string $metaInfo * @param string $icon Absolute path, must be 16*16 pixels * @param string $plainText Text that is used in the plain text email * if empty the $text is used, if false none will be used * @param string $plainMetaInfo Meta info that is used in the plain text email * if empty the $metaInfo is used, if false none will be used * @since 12.0.0 */ public function addBodyListItem($text, $metaInfo = '', $icon = '', $plainText = '', $plainMetaInfo = ''); /** * Adds a button group of two buttons to the body of the email * * @param string $textLeft Text of left button * @param string $urlLeft URL of left button * @param string $textRight Text of right button * @param string $urlRight URL of right button * @param string $plainTextLeft Text of left button that is used in the plain text version - if empty the $textLeft is used * @param string $plainTextRight Text of right button that is used in the plain text version - if empty the $textRight is used * * @since 12.0.0 */ public function addBodyButtonGroup($textLeft, $urlLeft, $textRight, $urlRight, $plainTextLeft = '', $plainTextRight = ''); /** * Adds a button to the body of the email * * @param string $text Text of button * @param string $url URL of button * @param string $plainText Text of button in plain text version * if empty the $text is used, if false none will be used * * @since 12.0.0 */ public function addBodyButton($text, $url, $plainText = ''); /** * Adds a logo and a text to the footer. <br> in the text will be replaced by new lines in the plain text email * * @param string $text If the text is empty the default "Name - Slogan<br>This is an automatically sent email" will be used * * @since 12.0.0 */ public function addFooter($text = ''); /** * Returns the rendered HTML email as string * * @return string * * @since 12.0.0 */ public function renderHtml(); /** * Returns the rendered plain text email as string * * @return string * * @since 12.0.0 */ public function renderText(); } public/Response.php 0000604 00000010143 15247130451 0010326 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Frank Karlitschek <frank@karlitschek.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Response Class. * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides convenient functions to send the correct http response headers * @since 4.0.0 * @deprecated 8.1.0 - Use AppFramework controllers instead and modify the response object */ class Response { /** * Enable response caching by sending correct HTTP headers * @param int $cache_time time to cache the response * >0 cache time in seconds * 0 and <0 enable default browser caching * null cache indefinitely * @since 4.0.0 */ static public function enableCaching( $cache_time = null ) { \OC_Response::enableCaching( $cache_time ); } /** * Checks and set Last-Modified header, when the request matches sends a * 'not modified' response * @param string $lastModified time when the response was last modified * @since 4.0.0 */ static public function setLastModifiedHeader( $lastModified ) { \OC_Response::setLastModifiedHeader( $lastModified ); } /** * Sets the content disposition header (with possible workarounds) * @param string $filename file name * @param string $type disposition type, either 'attachment' or 'inline' * @since 7.0.0 */ static public function setContentDispositionHeader( $filename, $type = 'attachment' ) { \OC_Response::setContentDispositionHeader( $filename, $type ); } /** * Sets the content length header (with possible workarounds) * @param string|int|float $length Length to be sent * @since 8.1.0 */ static public function setContentLengthHeader($length) { \OC_Response::setContentLengthHeader($length); } /** * Disable browser caching * @see enableCaching with cache_time = 0 * @since 4.0.0 */ static public function disableCaching() { \OC_Response::disableCaching(); } /** * Checks and set ETag header, when the request matches sends a * 'not modified' response * @param string $etag token to use for modification check * @since 4.0.0 */ static public function setETagHeader( $etag ) { \OC_Response::setETagHeader( $etag ); } /** * Send file as response, checking and setting caching headers * @param string $filepath of file to send * @since 4.0.0 * @deprecated 8.1.0 - Use \OCP\AppFramework\Http\StreamResponse or another AppFramework controller instead */ static public function sendFile( $filepath ) { \OC_Response::sendFile( $filepath ); } /** * Set response expire time * @param string|\DateTime $expires date-time when the response expires * string for DateInterval from now * DateTime object when to expire response * @since 4.0.0 */ static public function setExpiresHeader( $expires ) { \OC_Response::setExpiresHeader( $expires ); } /** * Send redirect response * @param string $location to redirect to * @since 4.0.0 */ static public function redirect( $location ) { \OC_Response::redirect( $location ); } } public/JSON.php 0000604 00000014550 15247130451 0007307 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Frank Karlitschek <frank@karlitschek.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * JSON Class */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides convenient functions to generate and send JSON data. Useful for Ajax calls * @deprecated 8.1.0 Use a AppFramework JSONResponse instead */ class JSON { /** * Encode and print $data in JSON format * @param array $data The data to use * @param bool $setContentType the optional content type * @deprecated 8.1.0 Use a AppFramework JSONResponse instead */ public static function encodedPrint( $data, $setContentType=true ) { \OC_JSON::encodedPrint($data, $setContentType); } /** * Check if the user is logged in, send json error msg if not. * * This method checks if a user is logged in. If not, a json error * response will be return and the method will exit from execution * of the script. * The returned json will be in the format: * * {"status":"error","data":{"message":"Authentication error."}} * * Add this call to the start of all ajax method files that requires * an authenticated user. * @deprecated 8.1.0 Use annotation based ACLs from the AppFramework instead */ public static function checkLoggedIn() { \OC_JSON::checkLoggedIn(); } /** * Check an ajax get/post call if the request token is valid. * * This method checks for a valid variable 'requesttoken' in $_GET, * $_POST and $_SERVER. If a valid token is not found, a json error * response will be return and the method will exit from execution * of the script. * The returned json will be in the format: * * {"status":"error","data":{"message":"Token expired. Please reload page."}} * * Add this call to the start of all ajax method files that creates, * updates or deletes anything. * In cases where you e.g. use an ajax call to load a dialog containing * a submittable form, you will need to add the requesttoken first as a * parameter to the ajax call, then assign it to the template and finally * add a hidden input field also named 'requesttoken' containing the value. * @deprecated 8.1.0 Use annotation based CSRF checks from the AppFramework instead */ public static function callCheck() { \OC_JSON::callCheck(); } /** * Send json success msg * * Return a json success message with optional extra data. * @see OCP\JSON::error() for the format to use. * * @param array $data The data to use * @return string json formatted string. * @deprecated 8.1.0 Use a AppFramework JSONResponse instead */ public static function success( $data = array() ) { \OC_JSON::success($data); } /** * Send json error msg * * Return a json error message with optional extra data for * error message or app specific data. * * Example use: * * $id = [some value] * OCP\JSON::error(array('data':array('message':'An error happened', 'id': $id))); * * Will return the json formatted string: * * {"status":"error","data":{"message":"An error happened", "id":[some value]}} * * @param array $data The data to use * @return string json formatted error string. * @deprecated 8.1.0 Use a AppFramework JSONResponse instead */ public static function error( $data = array() ) { \OC_JSON::error( $data ); } /** * Set Content-Type header to jsonrequest * @param string $type The content type header * @deprecated 8.1.0 Use a AppFramework JSONResponse instead */ public static function setContentTypeHeader( $type='application/json' ) { \OC_JSON::setContentTypeHeader($type); } /** * Check if the App is enabled and send JSON error message instead * * This method checks if a specific app is enabled. If not, a json error * response will be return and the method will exit from execution * of the script. * The returned json will be in the format: * * {"status":"error","data":{"message":"Application is not enabled."}} * * Add this call to the start of all ajax method files that requires * a specific app to be enabled. * * @param string $app The app to check * @deprecated 8.1.0 Use the AppFramework instead. It will automatically check if the app is enabled. */ public static function checkAppEnabled( $app ) { \OC_JSON::checkAppEnabled($app); } /** * Check if the user is a admin, send json error msg if not * * This method checks if the current user has admin rights. If not, a json error * response will be return and the method will exit from execution * of the script. * The returned json will be in the format: * * {"status":"error","data":{"message":"Authentication error."}} * * Add this call to the start of all ajax method files that requires * administrative rights. * * @deprecated 8.1.0 Use annotation based ACLs from the AppFramework instead */ public static function checkAdminUser() { \OC_JSON::checkAdminUser(); } /** * Encode JSON * @param array $data * @return string * @deprecated 8.1.0 Use a AppFramework JSONResponse instead */ public static function encode($data) { return \OC_JSON::encode($data); } /** * Check is a given user exists - send json error msg if not * @param string $user * @deprecated 8.1.0 Use a AppFramework JSONResponse instead */ public static function checkUserExists($user) { \OC_JSON::checkUserExists($user); } } public/IUserManager.php 0000604 00000010234 15247130451 0011053 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Class Manager * * Hooks available in scope \OC\User: * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword) * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword) * - preDelete(\OC\User\User $user) * - postDelete(\OC\User\User $user) * - preCreateUser(string $uid, string $password) * - postCreateUser(\OC\User\User $user, string $password) * * @package OC\User * @since 8.0.0 */ interface IUserManager { /** * register a user backend * * @param \OCP\UserInterface $backend * @since 8.0.0 */ public function registerBackend($backend); /** * Get the active backends * @return \OCP\UserInterface[] * @since 8.0.0 */ public function getBackends(); /** * remove a user backend * * @param \OCP\UserInterface $backend * @since 8.0.0 */ public function removeBackend($backend); /** * remove all user backends * @since 8.0.0 */ public function clearBackends() ; /** * get a user by user id * * @param string $uid * @return \OCP\IUser|null Either the user or null if the specified user does not exist * @since 8.0.0 */ public function get($uid); /** * check if a user exists * * @param string $uid * @return bool * @since 8.0.0 */ public function userExists($uid); /** * Check if the password is valid for the user * * @param string $loginName * @param string $password * @return mixed the User object on success, false otherwise * @since 8.0.0 */ public function checkPassword($loginName, $password); /** * search by user id * * @param string $pattern * @param int $limit * @param int $offset * @return \OCP\IUser[] * @since 8.0.0 */ public function search($pattern, $limit = null, $offset = null); /** * search by displayName * * @param string $pattern * @param int $limit * @param int $offset * @return \OCP\IUser[] * @since 8.0.0 */ public function searchDisplayName($pattern, $limit = null, $offset = null); /** * @param string $uid * @param string $password * @throws \InvalidArgumentException * @return bool|\OCP\IUser the created user of false * @since 8.0.0 */ public function createUser($uid, $password); /** * @param string $uid * @param string $password * @param UserInterface $backend * @return IUser|null * @throws \InvalidArgumentException * @since 12.0.0 */ public function createUserFromBackend($uid, $password, UserInterface $backend); /** * returns how many users per backend exist (if supported by backend) * * @return array an array of backend class as key and count number as value * @since 8.0.0 */ public function countUsers(); /** * @param \Closure $callback * @param string $search * @since 9.0.0 */ public function callForAllUsers(\Closure $callback, $search = ''); /** * returns how many users have logged in once * * @return int * @since 11.0.0 */ public function countDisabledUsers(); /** * returns how many users have logged in once * * @return int * @since 11.0.0 */ public function countSeenUsers(); /** * @param \Closure $callback * @since 11.0.0 */ public function callForSeenUsers(\Closure $callback); /** * @param string $email * @return IUser[] * @since 9.1.0 */ public function getByEmail($email); } public/ICertificate.php 0000604 00000003030 15247130451 0011060 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface ICertificate * * @package OCP * @since 8.0.0 */ interface ICertificate { /** * @return string * @since 8.0.0 */ public function getName(); /** * @return string * @since 8.0.0 */ public function getCommonName(); /** * @return string * @since 8.0.0 */ public function getOrganization(); /** * @return \DateTime * @since 8.0.0 */ public function getIssueDate(); /** * @return \DateTime * @since 8.0.0 */ public function getExpireDate(); /** * @return bool * @since 8.0.0 */ public function isExpired(); /** * @return string * @since 8.0.0 */ public function getIssuerName(); /** * @return string * @since 8.0.0 */ public function getIssuerOrganization(); } public/Contacts/ContactsMenu/IActionFactory.php 0000604 00000003122 15247130451 0015566 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Contacts\ContactsMenu; /** * @since 12.0 */ interface IActionFactory { /** * Construct and return a new link action for the contacts menu * * @since 12.0 * * @param string $icon full path to the action's icon * @param string $name localized name of the action * @param string $href target URL * @return ILinkAction */ public function newLinkAction($icon, $name, $href); /** * Construct and return a new email action for the contacts menu * * @since 12.0 * * @param string $icon full path to the action's icon * @param string $name localized name of the action * @param string $email target e-mail address * @return ILinkAction */ public function newEMailAction($icon, $name, $email); } public/Contacts/ContactsMenu/IProvider.php 0000604 00000002057 15247130451 0014621 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Contacts\ContactsMenu; /** * @since 12.0 */ interface IProvider { /** * @since 12.0 * @param IEntry $entry * @return void */ public function process(IEntry $entry); } public/Contacts/ContactsMenu/ILinkAction.php 0000604 00000002216 15247130451 0015057 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Contacts\ContactsMenu; /** * @since 12.0 */ interface ILinkAction extends IAction { /** * @since 12.0 * @param string $href the target URL of the action */ public function setHref($href); /** * @since 12.0 * @return string */ public function getHref(); } public/Contacts/ContactsMenu/IAction.php 0000604 00000003237 15247130451 0014245 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Contacts\ContactsMenu; use JsonSerializable; /** * Apps should use the IActionFactory to create new action objects * * @since 12.0 */ interface IAction extends JsonSerializable { /** * @param string $icon absolute URI to an icon * @since 12.0 */ public function setIcon($icon); /** * @return string localized action name, e.g. 'Call' * @since 12.0 */ public function getName(); /** * @param string $name localized action name, e.g. 'Call' * @since 12.0 */ public function setName($name); /** * @param int $priority priorize actions, high order ones are shown on top * @since 12.0 */ public function setPriority($priority); /** * @return int priority to priorize actions, high order ones are shown on top * @since 12.0 */ public function getPriority(); } public/Contacts/ContactsMenu/IEntry.php 0000604 00000003064 15247130451 0014127 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCP\Contacts\ContactsMenu; use JsonSerializable; /** * @since 12.0 */ interface IEntry extends JsonSerializable { /** * @since 12.0 * @return string */ public function getFullName(); /** * @since 12.0 * @return string[] */ public function getEMailAddresses(); /** * @since 12.0 * @return string|null image URI */ public function getAvatar(); /** * @since 12.0 * @param IAction $action an action to show in the contacts menu */ public function addAction(IAction $action); /** * Get an arbitrary property from the contact * * @since 12.0 * @param string $key * @return mixed the value of the property or null */ public function getProperty($key); } public/Contacts/IManager.php 0000604 00000012275 15247130451 0012001 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Contacts Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP\Contacts { /** * This class provides access to the contacts app. Use this class exclusively if you want to access contacts. * * Contacts in general will be expressed as an array of key-value-pairs. * The keys will match the property names defined in https://tools.ietf.org/html/rfc2426#section-1 * * Proposed workflow for working with contacts: * - search for the contacts * - manipulate the results array * - createOrUpdate will save the given contacts overwriting the existing data * * For updating it is mandatory to keep the id. * Without an id a new contact will be created. * * @since 6.0.0 */ interface IManager { /** * This function is used to search and find contacts within the users address books. * In case $pattern is empty all contacts will be returned. * * Example: * Following function shows how to search for contacts for the name and the email address. * * public static function getMatchingRecipient($term) { * $cm = \OC::$server->getContactsManager(); * // The API is not active -> nothing to do * if (!$cm->isEnabled()) { * return array(); * } * * $result = $cm->search($term, array('FN', 'EMAIL')); * $receivers = array(); * foreach ($result as $r) { * $id = $r['id']; * $fn = $r['FN']; * $email = $r['EMAIL']; * if (!is_array($email)) { * $email = array($email); * } * * // loop through all email addresses of this contact * foreach ($email as $e) { * $displayName = $fn . " <$e>"; * $receivers[] = array( * 'id' => $id, * 'label' => $displayName, * 'value' => $displayName); * } * } * * return $receivers; * } * * * @param string $pattern which should match within the $searchProperties * @param array $searchProperties defines the properties within the query pattern should match * @param array $options - for future use. One should always have options! * @return array an array of contacts which are arrays of key-value-pairs * @since 6.0.0 */ function search($pattern, $searchProperties = array(), $options = array()); /** * This function can be used to delete the contact identified by the given id * * @param object $id the unique identifier to a contact * @param string $address_book_key identifier of the address book in which the contact shall be deleted * @return bool successful or not * @since 6.0.0 */ function delete($id, $address_book_key); /** * This function is used to create a new contact if 'id' is not given or not present. * Otherwise the contact will be updated by replacing the entire data set. * * @param array $properties this array if key-value-pairs defines a contact * @param string $address_book_key identifier of the address book in which the contact shall be created or updated * @return array an array representing the contact just created or updated * @since 6.0.0 */ function createOrUpdate($properties, $address_book_key); /** * Check if contacts are available (e.g. contacts app enabled) * * @return bool true if enabled, false if not * @since 6.0.0 */ function isEnabled(); /** * Registers an address book * * @param \OCP\IAddressBook $address_book * @return void * @since 6.0.0 */ function registerAddressBook(\OCP\IAddressBook $address_book); /** * Unregisters an address book * * @param \OCP\IAddressBook $address_book * @return void * @since 6.0.0 */ function unregisterAddressBook(\OCP\IAddressBook $address_book); /** * In order to improve lazy loading a closure can be registered which will be called in case * address books are actually requested * * @param \Closure $callable * @return void * @since 6.0.0 */ function register(\Closure $callable); /** * @return array * @since 6.0.0 */ function getAddressBooks(); /** * removes all registered address book instances * @return void * @since 6.0.0 */ function clear(); } } public/Federation/ICloudIdManager.php 0000604 00000002615 15247130451 0013544 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Federation; /** * Interface for resolving federated cloud ids * * @since 12.0.0 */ interface ICloudIdManager { /** * @param string $cloudId * @return ICloudId * @throws \InvalidArgumentException * * @since 12.0.0 */ public function resolveCloudId($cloudId); /** * Get the cloud id for a remote user * * @param string $user * @param string $remote * @return ICloudId * * @since 12.0.0 */ public function getCloudId($user, $remote); /** * Check if the input is a correctly formatted cloud id * * @param string $cloudId * @return bool * * @since 12.0.0 */ public function isValidCloudId($cloudId); } public/Federation/ICloudId.php 0000604 00000002505 15247130451 0012247 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Federation; /** * Parsed federated cloud id * * @since 12.0.0 */ interface ICloudId { /** * The remote cloud id * * @return string * @since 12.0.0 */ public function getId(); /** * Get a clean representation of the cloud id for display * * @return string * @since 12.0.0 */ public function getDisplayId(); /** * The username on the remote server * * @return string * @since 12.0.0 */ public function getUser(); /** * The base address of the remote server * * @return string * @since 12.0.0 */ public function getRemote(); } public/App/ManagerEvent.php 0000604 00000004035 15247130451 0011627 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\App; use Symfony\Component\EventDispatcher\Event; /** * Class ManagerEvent * * @package OCP\APP * @since 9.0.0 */ class ManagerEvent extends Event { const EVENT_APP_ENABLE = 'OCP\App\IAppManager::enableApp'; const EVENT_APP_ENABLE_FOR_GROUPS = 'OCP\App\IAppManager::enableAppForGroups'; const EVENT_APP_DISABLE = 'OCP\App\IAppManager::disableApp'; /** * @since 9.1.0 */ const EVENT_APP_UPDATE = 'OCP\App\IAppManager::updateApp'; /** @var string */ protected $event; /** @var string */ protected $appID; /** @var \OCP\IGroup[] */ protected $groups; /** * DispatcherEvent constructor. * * @param string $event * @param $appID * @param \OCP\IGroup[] $groups * @since 9.0.0 */ public function __construct($event, $appID, array $groups = null) { $this->event = $event; $this->appID = $appID; $this->groups = $groups; } /** * @return string * @since 9.0.0 */ public function getEvent() { return $this->event; } /** * @return string * @since 9.0.0 */ public function getAppID() { return $this->appID; } /** * returns the group Ids * @return string[] * @since 9.0.0 */ public function getGroups() { return array_map(function ($group) { /** @var \OCP\IGroup $group */ return $group->getGID(); }, $this->groups); } } public/App/IAppManager.php 0000604 00000005554 15247130451 0011406 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\App; use OCP\IUser; /** * Interface IAppManager * * @package OCP\App * @since 8.0.0 */ interface IAppManager { /** * Check if an app is enabled for user * * @param string $appId * @param \OCP\IUser $user (optional) if not defined, the currently loggedin user will be used * @return bool * @since 8.0.0 */ public function isEnabledForUser($appId, $user = null); /** * Check if an app is installed in the instance * * @param string $appId * @return bool * @since 8.0.0 */ public function isInstalled($appId); /** * Enable an app for every user * * @param string $appId * @throws AppPathNotFoundException * @since 8.0.0 */ public function enableApp($appId); /** * Whether a list of types contains a protected app type * * @param string[] $types * @return bool * @since 12.0.0 */ public function hasProtectedAppType($types); /** * Enable an app only for specific groups * * @param string $appId * @param \OCP\IGroup[] $groups * @since 8.0.0 */ public function enableAppForGroups($appId, $groups); /** * Disable an app for every user * * @param string $appId * @since 8.0.0 */ public function disableApp($appId); /** * Get the directory for the given app. * * @param string $appId * @return string * @since 11.0.0 * @throws AppPathNotFoundException */ public function getAppPath($appId); /** * List all apps enabled for a user * * @param \OCP\IUser $user * @return string[] * @since 8.1.0 */ public function getEnabledAppsForUser(IUser $user); /** * List all installed apps * * @return string[] * @since 8.1.0 */ public function getInstalledApps(); /** * Clear the cached list of apps when enabling/disabling an app * @since 8.1.0 */ public function clearAppsCache(); /** * @param string $appId * @return boolean * @since 9.0.0 */ public function isShipped($appId); /** * @return string[] * @since 9.0.0 */ public function getAlwaysEnabledApps(); } public/App/AppPathNotFoundException.php 0000604 00000001631 15247130451 0014143 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net> * * @author Julius Härtl <jus@bitgrid.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\App; /** * Class AppPathNotFoundException * * @package OCP\App * @since 11.0.0 */ class AppPathNotFoundException extends \Exception {} public/Contacts.php 0000604 00000014632 15247130451 0010315 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Contacts Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP { /** * This class provides access to the contacts app. Use this class exclusively if you want to access contacts. * * Contacts in general will be expressed as an array of key-value-pairs. * The keys will match the property names defined in https://tools.ietf.org/html/rfc2426#section-1 * * Proposed workflow for working with contacts: * - search for the contacts * - manipulate the results array * - createOrUpdate will save the given contacts overwriting the existing data * * For updating it is mandatory to keep the id. * Without an id a new contact will be created. * * @deprecated 8.1.0 use methods of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ class Contacts { /** * This function is used to search and find contacts within the users address books. * In case $pattern is empty all contacts will be returned. * * Example: * Following function shows how to search for contacts for the name and the email address. * * public static function getMatchingRecipient($term) { * // The API is not active -> nothing to do * if (!\OCP\Contacts::isEnabled()) { * return array(); * } * * $result = \OCP\Contacts::search($term, array('FN', 'EMAIL')); * $receivers = array(); * foreach ($result as $r) { * $id = $r['id']; * $fn = $r['FN']; * $email = $r['EMAIL']; * if (!is_array($email)) { * $email = array($email); * } * * // loop through all email addresses of this contact * foreach ($email as $e) { * $displayName = $fn . " <$e>"; * $receivers[] = array( * 'id' => $id, * 'label' => $displayName, * 'value' => $displayName); * } * } * * return $receivers; * } * * * @param string $pattern which should match within the $searchProperties * @param array $searchProperties defines the properties within the query pattern should match * @param array $options - for future use. One should always have options! * @return array an array of contacts which are arrays of key-value-pairs * @deprecated 8.1.0 use search() of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ public static function search($pattern, $searchProperties = array(), $options = array()) { $cm = \OC::$server->getContactsManager(); return $cm->search($pattern, $searchProperties, $options); } /** * This function can be used to delete the contact identified by the given id * * @param object $id the unique identifier to a contact * @param string $address_book_key * @return bool successful or not * @deprecated 8.1.0 use delete() of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ public static function delete($id, $address_book_key) { $cm = \OC::$server->getContactsManager(); return $cm->delete($id, $address_book_key); } /** * This function is used to create a new contact if 'id' is not given or not present. * Otherwise the contact will be updated by replacing the entire data set. * * @param array $properties this array if key-value-pairs defines a contact * @param string $address_book_key identifier of the address book in which the contact shall be created or updated * @return array an array representing the contact just created or updated * @deprecated 8.1.0 use createOrUpdate() of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ public static function createOrUpdate($properties, $address_book_key) { $cm = \OC::$server->getContactsManager(); return $cm->createOrUpdate($properties, $address_book_key); } /** * Check if contacts are available (e.g. contacts app enabled) * * @return bool true if enabled, false if not * @deprecated 8.1.0 use isEnabled() of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ public static function isEnabled() { $cm = \OC::$server->getContactsManager(); return $cm->isEnabled(); } /** * @param \OCP\IAddressBook $address_book * @deprecated 8.1.0 use registerAddressBook() of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ public static function registerAddressBook(\OCP\IAddressBook $address_book) { $cm = \OC::$server->getContactsManager(); $cm->registerAddressBook($address_book); } /** * @param \OCP\IAddressBook $address_book * @deprecated 8.1.0 use unregisterAddressBook() of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ public static function unregisterAddressBook(\OCP\IAddressBook $address_book) { $cm = \OC::$server->getContactsManager(); $cm->unregisterAddressBook($address_book); } /** * @return array * @deprecated 8.1.0 use getAddressBooks() of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ public static function getAddressBooks() { $cm = \OC::$server->getContactsManager(); return $cm->getAddressBooks(); } /** * removes all registered address book instances * @deprecated 8.1.0 use clear() of \OCP\Contacts\IManager - \OC::$server->getContactsManager(); * @since 5.0.0 */ public static function clear() { $cm = \OC::$server->getContactsManager(); $cm->clear(); } } } public/ISession.php 0000604 00000004727 15247130451 0010277 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Session interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * Interface ISession * * wrap PHP's internal session handling into the ISession interface * @since 6.0.0 */ interface ISession { /** * Set a value in the session * * @param string $key * @param mixed $value * @since 6.0.0 */ public function set($key, $value); /** * Get a value from the session * * @param string $key * @return mixed should return null if $key does not exist * @since 6.0.0 */ public function get($key); /** * Check if a named key exists in the session * * @param string $key * @return bool * @since 6.0.0 */ public function exists($key); /** * Remove a $key/$value pair from the session * * @param string $key * @since 6.0.0 */ public function remove($key); /** * Reset and recreate the session * @since 6.0.0 */ public function clear(); /** * Close the session and release the lock * @since 7.0.0 */ public function close(); /** * Wrapper around session_regenerate_id * * @param bool $deleteOldSession Whether to delete the old associated session file or not. * @return void * @since 9.0.0 */ public function regenerateId($deleteOldSession = true); /** * Wrapper around session_id * * @return string * @throws SessionNotAvailableException * @since 9.1.0 */ public function getId(); } public/IGroup.php 0000604 00000004567 15247130451 0007752 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <PVince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP; /** * Interface IGroup * * @package OCP * @since 8.0.0 */ interface IGroup { /** * @return string * @since 8.0.0 */ public function getGID(); /** * Returns the group display name * * @return string * @since 12.0.0 */ public function getDisplayName(); /** * get all users in the group * * @return \OCP\IUser[] * @since 8.0.0 */ public function getUsers(); /** * check if a user is in the group * * @param \OCP\IUser $user * @return bool * @since 8.0.0 */ public function inGroup($user); /** * add a user to the group * * @param \OCP\IUser $user * @since 8.0.0 */ public function addUser($user); /** * remove a user from the group * * @param \OCP\IUser $user * @since 8.0.0 */ public function removeUser($user); /** * search for users in the group by userid * * @param string $search * @param int $limit * @param int $offset * @return \OCP\IUser[] * @since 8.0.0 */ public function searchUsers($search, $limit = null, $offset = null); /** * returns the number of users matching the search string * * @param string $search * @return int|bool * @since 8.0.0 */ public function count($search = ''); /** * search for users in the group by displayname * * @param string $search * @param int $limit * @param int $offset * @return \OCP\IUser[] * @since 8.0.0 */ public function searchDisplayName($search, $limit = null, $offset = null); /** * delete the group * * @return bool * @since 8.0.0 */ public function delete(); } public/Files.php 0000604 00000007550 15247130451 0007602 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Files Class * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This class provides access to the internal filesystem abstraction layer. Use * this class exlusively if you want to access files * @since 5.0.0 */ class Files { /** * Recusive deletion of folders * @return bool * @since 5.0.0 */ static function rmdirr( $dir ) { return \OC_Helper::rmdirr( $dir ); } /** * Get the mimetype form a local file * @param string $path * @return string * does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead * @since 5.0.0 */ static function getMimeType( $path ) { return \OC::$server->getMimeTypeDetector()->detect($path); } /** * Search for files by mimetype * @param string $mimetype * @return array * @since 6.0.0 */ static public function searchByMime( $mimetype ) { return(\OC\Files\Filesystem::searchByMime( $mimetype )); } /** * Copy the contents of one stream to another * @param resource $source * @param resource $target * @return int the number of bytes copied * @since 5.0.0 */ public static function streamCopy( $source, $target ) { list($count, ) = \OC_Helper::streamCopy( $source, $target ); return $count; } /** * Create a temporary file with an unique filename * @param string $postfix * @return string * * temporary files are automatically cleaned up after the script is finished * @deprecated 8.1.0 use getTemporaryFile() of \OCP\ITempManager - \OC::$server->getTempManager() * @since 5.0.0 */ public static function tmpFile( $postfix='' ) { return \OC::$server->getTempManager()->getTemporaryFile($postfix); } /** * Create a temporary folder with an unique filename * @return string * * temporary files are automatically cleaned up after the script is finished * @deprecated 8.1.0 use getTemporaryFolder() of \OCP\ITempManager - \OC::$server->getTempManager() * @since 5.0.0 */ public static function tmpFolder() { return \OC::$server->getTempManager()->getTemporaryFolder(); } /** * Adds a suffix to the name in case the file exists * @param string $path * @param string $filename * @return string * @since 5.0.0 */ public static function buildNotExistingFileName( $path, $filename ) { return(\OC_Helper::buildNotExistingFileName( $path, $filename )); } /** * Gets the Storage for an app - creates the needed folder if they are not * existent * @param string $app * @return \OC\Files\View * @since 5.0.0 */ public static function getStorage( $app ) { return \OC_App::getStorage( $app ); } } public/IMemcache.php 0000604 00000004077 15247130451 0010354 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Public interface of ownCloud for apps to use. * Cache interface * */ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; /** * This interface defines method for accessing the file based user cache. * * @since 8.1.0 */ interface IMemcache extends ICache { /** * Set a value in the cache if it's not already stored * * @param string $key * @param mixed $value * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 * @return bool * @since 8.1.0 */ public function add($key, $value, $ttl = 0); /** * Increase a stored number * * @param string $key * @param int $step * @return int | bool * @since 8.1.0 */ public function inc($key, $step = 1); /** * Decrease a stored number * * @param string $key * @param int $step * @return int | bool * @since 8.1.0 */ public function dec($key, $step = 1); /** * Compare and set * * @param string $key * @param mixed $old * @param mixed $new * @return bool * @since 8.1.0 */ public function cas($key, $old, $new); /** * Compare and delete * * @param string $key * @param mixed $old * @return bool * @since 8.1.0 */ public function cad($key, $old); } public/Preview/IProvider.php 0000604 00000003501 15247130451 0012054 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Preview; /** * Interface IProvider * * @package OCP\Preview * @since 8.1.0 */ interface IProvider { /** * @return string Regex with the mimetypes that are supported by this provider * @since 8.1.0 */ public function getMimeType(); /** * Check if a preview can be generated for $path * * @param \OCP\Files\FileInfo $file * @return bool * @since 8.1.0 */ public function isAvailable(\OCP\Files\FileInfo $file); /** * get thumbnail for file at path $path * * @param string $path Path of file * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image * @param bool $scalingup Disable/Enable upscaling of previews * @param \OC\Files\View $fileview fileview object of user folder * @return bool|\OCP\IImage false if no preview was generated * @since 8.1.0 */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview); } public/Http/Client/IResponse.php 0000604 00000002427 15247130451 0012602 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Http\Client; /** * Interface IResponse * * @package OCP\Http * @since 8.1.0 */ interface IResponse { /** * @return string|resource * @since 8.1.0 */ public function getBody(); /** * @return int * @since 8.1.0 */ public function getStatusCode(); /** * @param $key * @return string * @since 8.1.0 */ public function getHeader($key); /** * @return array * @since 8.1.0 */ public function getHeaders(); } public/Http/Client/IClientService.php 0000604 00000001762 15247130451 0013544 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Http\Client; /** * Interface IClientService * * @package OCP\Http * @since 8.1.0 */ interface IClientService { /** * @return IClient * @since 8.1.0 */ public function newClient(); } public/Http/Client/IClient.php 0000604 00000017046 15247130451 0012225 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCP\Http\Client; /** * Interface IClient * * @package OCP\Http * @since 8.1.0 */ interface IClient { /** * Sends a GET request * @param string $uri * @param array $options Array such as * 'query' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function get($uri, array $options = []); /** * Sends a HEAD request * @param string $uri * @param array $options Array such as * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function head($uri, $options = []); /** * Sends a POST request * @param string $uri * @param array $options Array such as * 'body' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function post($uri, array $options = []); /** * Sends a PUT request * @param string $uri * @param array $options Array such as * 'body' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function put($uri, array $options = []); /** * Sends a DELETE request * @param string $uri * @param array $options Array such as * 'body' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function delete($uri, array $options = []); /** * Sends a options request * @param string $uri * @param array $options Array such as * 'body' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * @return IResponse * @throws \Exception If the request could not get completed * @since 8.1.0 */ public function options($uri, array $options = []); } private/Lockdown/LockdownManager.php 0000604 00000003675 15247130451 0013573 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Lockdown; use OC\Authentication\Token\IToken; use OCP\ISession; use OCP\Lockdown\ILockdownManager; class LockdownManager implements ILockdownManager { /** @var ISession */ private $sessionCallback; private $enabled = false; /** @var array|null */ private $scope; /** * LockdownManager constructor. * * @param callable $sessionCallback we need to inject the session lazily to avoid dependency loops */ public function __construct(callable $sessionCallback) { $this->sessionCallback = $sessionCallback; } public function enable() { $this->enabled = true; } /** * @return ISession */ private function getSession() { $callback = $this->sessionCallback; return $callback(); } private function getScopeAsArray() { if (!$this->scope) { $session = $this->getSession(); $sessionScope = $session->get('token_scope'); if ($sessionScope) { $this->scope = $sessionScope; } } return $this->scope; } public function setToken(IToken $token) { $this->scope = $token->getScopeAsArray(); $session = $this->getSession(); $session->set('token_scope', $this->scope); $this->enable(); } public function canAccessFilesystem() { $scope = $this->getScopeAsArray(); return !$scope || $scope['filesystem']; } } private/Lockdown/Filesystem/NullStorage.php 0000604 00000010616 15247130451 0015074 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Lockdown\Filesystem; use Icewind\Streams\IteratorDirectory; use OC\Files\FileInfo; use OC\Files\Storage\Common; class NullStorage extends Common { public function __construct($parameters) { parent::__construct($parameters); } public function getId() { return 'null'; } public function mkdir($path) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function rmdir($path) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function opendir($path) { return new IteratorDirectory([]); } public function is_dir($path) { return $path === ''; } public function is_file($path) { return false; } public function stat($path) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function filetype($path) { return ($path === '') ? 'dir' : false; } public function filesize($path) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function isCreatable($path) { return false; } public function isReadable($path) { return $path === ''; } public function isUpdatable($path) { return false; } public function isDeletable($path) { return false; } public function isSharable($path) { return false; } public function getPermissions($path) { return null; } public function file_exists($path) { return $path === ''; } public function filemtime($path) { return ($path === '') ? time() : false; } public function file_get_contents($path) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function file_put_contents($path, $data) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function unlink($path) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function rename($path1, $path2) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function copy($path1, $path2) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function fopen($path, $mode) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function getMimeType($path) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function hash($type, $path, $raw = false) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function free_space($path) { return FileInfo::SPACE_UNKNOWN; } public function touch($path, $mtime = null) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function getLocalFile($path) { return false; } public function hasUpdated($path, $time) { return false; } public function getETag($path) { return ''; } public function isLocal() { return false; } public function getDirectDownload($path) { return false; } public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function test() { return true; } public function getOwner($path) { return null; } public function getCache($path = '', $storage = null) { return new NullCache(); } } private/Lockdown/Filesystem/NullCache.php 0000604 00000005645 15247130451 0014501 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Lockdown\Filesystem; use OC\Files\Cache\CacheEntry; use OCP\Constants; use OCP\Files\Cache\ICache; use OCP\Files\FileInfo; use OCP\Files\Search\ISearchQuery; class NullCache implements ICache { public function getNumericStorageId() { return -1; } public function get($file) { return $file !== '' ? null : new CacheEntry([ 'fileid' => -1, 'parent' => -1, 'name' => '', 'path' => '', 'size' => '0', 'mtime' => time(), 'storage_mtime' => time(), 'etag' => '', 'mimetype' => FileInfo::MIMETYPE_FOLDER, 'mimepart' => 'httpd', 'permissions' => Constants::PERMISSION_READ ]); } public function getFolderContents($folder) { return []; } public function getFolderContentsById($fileId) { return []; } public function put($file, array $data) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function insert($file, array $data) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function update($id, array $data) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function getId($file) { return -1; } public function getParentId($file) { return -1; } public function inCache($file) { return $file === ''; } public function remove($file) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function move($source, $target) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); } public function getStatus($file) { return ICache::COMPLETE; } public function search($pattern) { return []; } public function searchByMime($mimetype) { return []; } public function searchQuery(ISearchQuery $query) { return []; } public function searchByTag($tag, $userId) { return []; } public function getIncomplete() { return []; } public function getPathById($id) { return ''; } public function normalize($path) { return $path; } } private/Console/Application.php 0000604 00000013752 15247130451 0012602 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Console; use OC\NeedsUpdateException; use OC_App; use OCP\AppFramework\QueryException; use OCP\Console\ConsoleEvent; use OCP\IConfig; use OCP\ILogger; use OCP\IRequest; use Symfony\Component\Console\Application as SymfonyApplication; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class Application { /** @var IConfig */ private $config; /** @var EventDispatcherInterface */ private $dispatcher; /** @var IRequest */ private $request; /** @var ILogger */ private $logger; /** * @param IConfig $config * @param EventDispatcherInterface $dispatcher * @param IRequest $request * @param ILogger $logger */ public function __construct(IConfig $config, EventDispatcherInterface $dispatcher, IRequest $request, ILogger $logger) { $defaults = \OC::$server->getThemingDefaults(); $this->config = $config; $this->application = new SymfonyApplication($defaults->getName(), \OC_Util::getVersionString()); $this->dispatcher = $dispatcher; $this->request = $request; $this->logger = $logger; } /** * @param InputInterface $input * @param OutputInterface $output * @throws \Exception */ public function loadCommands(InputInterface $input, OutputInterface $output) { // $application is required to be defined in the register_command scripts $application = $this->application; $inputDefinition = $application->getDefinition(); $inputDefinition->addOption( new InputOption( 'no-warnings', null, InputOption::VALUE_NONE, 'Skip global warnings, show command output only', null ) ); try { $input->bind($inputDefinition); } catch (\RuntimeException $e) { //expected if there are extra options } if ($input->getOption('no-warnings')) { $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); } try { require_once __DIR__ . '/../../../core/register_command.php'; if ($this->config->getSystemValue('installed', false)) { if (\OCP\Util::needUpgrade()) { throw new NeedsUpdateException(); } elseif ($this->config->getSystemValue('maintenance', false)) { if ($input->getArgument('command') !== '_completion') { $errOutput = $output->getErrorOutput(); $errOutput->writeln('<comment>Nextcloud is in maintenance mode - no app have been loaded</comment>' . PHP_EOL); } } else { OC_App::loadApps(); foreach (\OC::$server->getAppManager()->getInstalledApps() as $app) { $appPath = \OC_App::getAppPath($app); if ($appPath === false) { continue; } // load commands using info.xml $info = \OC_App::getAppInfo($app); if (isset($info['commands'])) { $this->loadCommandsFromInfoXml($info['commands']); } // load from register_command.php \OC_App::registerAutoloading($app, $appPath); $file = $appPath . '/appinfo/register_command.php'; if (file_exists($file)) { try { require $file; } catch (\Exception $e) { $this->logger->logException($e); } } } } } else if ($input->getArgument('command') !== '_completion') { $output->writeln("Nextcloud is not installed - only a limited number of commands are available"); } } catch(NeedsUpdateException $e) { if ($input->getArgument('command') !== '_completion') { $output->writeln("Nextcloud or one of the apps require upgrade - only a limited number of commands are available"); $output->writeln("You may use your browser or the occ upgrade command to do the upgrade"); } } if ($input->getFirstArgument() !== 'check') { $errors = \OC_Util::checkServer(\OC::$server->getSystemConfig()); if (!empty($errors)) { foreach ($errors as $error) { $output->writeln((string)$error['error']); $output->writeln((string)$error['hint']); $output->writeln(''); } throw new \Exception("Environment not properly prepared."); } } } /** * Sets whether to automatically exit after a command execution or not. * * @param bool $boolean Whether to automatically exit after a command execution or not */ public function setAutoExit($boolean) { $this->application->setAutoExit($boolean); } /** * @param InputInterface $input * @param OutputInterface $output * @return int * @throws \Exception */ public function run(InputInterface $input = null, OutputInterface $output = null) { $this->dispatcher->dispatch(ConsoleEvent::EVENT_RUN, new ConsoleEvent( ConsoleEvent::EVENT_RUN, $this->request->server['argv'] )); return $this->application->run($input, $output); } private function loadCommandsFromInfoXml($commands) { foreach ($commands as $command) { try { $c = \OC::$server->query($command); } catch (QueryException $e) { if (class_exists($command)) { $c = new $command(); } else { throw new \Exception("Console command '$command' is unknown and could not be loaded"); } } $this->application->add($c); } } } private/Console/TimestampFormatter.php 0000604 00000005670 15247130451 0014166 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Console; use OCP\IConfig; use Symfony\Component\Console\Formatter\OutputFormatterInterface; use Symfony\Component\Console\Formatter\OutputFormatterStyleInterface; class TimestampFormatter implements OutputFormatterInterface { /** @var IConfig */ protected $config; /** * @param IConfig $config * @param OutputFormatterInterface $formatter */ public function __construct(IConfig $config, OutputFormatterInterface $formatter) { $this->config = $config; $this->formatter = $formatter; } /** * Sets the decorated flag. * * @param bool $decorated Whether to decorate the messages or not */ public function setDecorated($decorated) { $this->formatter->setDecorated($decorated); } /** * Gets the decorated flag. * * @return bool true if the output will decorate messages, false otherwise */ public function isDecorated() { return $this->formatter->isDecorated(); } /** * Sets a new style. * * @param string $name The style name * @param OutputFormatterStyleInterface $style The style instance */ public function setStyle($name, OutputFormatterStyleInterface $style) { $this->formatter->setStyle($name, $style); } /** * Checks if output formatter has style with specified name. * * @param string $name * @return bool */ public function hasStyle($name) { $this->formatter->hasStyle($name); } /** * Gets style options from style with specified name. * * @param string $name * @return OutputFormatterStyleInterface */ public function getStyle($name) { return $this->formatter->getStyle($name); } /** * Formats a message according to the given styles. * * @param string $message The message to style * @return string The styled message, prepended with a timestamp using the * log timezone and dateformat, e.g. "2015-06-23T17:24:37+02:00" */ public function format($message) { $timeZone = $this->config->getSystemValue('logtimezone', 'UTC'); $timeZone = $timeZone !== null ? new \DateTimeZone($timeZone) : null; $time = new \DateTime('now', $timeZone); $timestampInfo = $time->format($this->config->getSystemValue('logdateformat', \DateTime::ATOM)); return $timestampInfo . ' ' . $this->formatter->format($message); } } private/RedisFactory.php 0000604 00000005333 15247130451 0011327 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class RedisFactory { /** @var \Redis */ private $instance; /** @var SystemConfig */ private $config; /** * RedisFactory constructor. * * @param SystemConfig $config */ public function __construct(SystemConfig $config) { $this->config = $config; } private function create() { if ($config = $this->config->getValue('redis.cluster', [])) { if (!class_exists('RedisCluster')) { throw new \Exception('Redis Cluster support is not available'); } // cluster config if (isset($config['timeout'])) { $timeout = $config['timeout']; } else { $timeout = null; } if (isset($config['read_timeout'])) { $readTimeout = $config['read_timeout']; } else { $readTimeout = null; } $this->instance = new \RedisCluster(null, $config['seeds'], $timeout, $readTimeout); if (isset($config['failover_mode'])) { $this->instance->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, $config['failover_mode']); } } else { $this->instance = new \Redis(); $config = $this->config->getValue('redis', []); if (isset($config['host'])) { $host = $config['host']; } else { $host = '127.0.0.1'; } if (isset($config['port'])) { $port = $config['port']; } else { $port = 6379; } if (isset($config['timeout'])) { $timeout = $config['timeout']; } else { $timeout = 0.0; // unlimited } $this->instance->connect($host, $port, $timeout); if (isset($config['password']) && $config['password'] !== '') { $this->instance->auth($config['password']); } if (isset($config['dbindex'])) { $this->instance->select($config['dbindex']); } } } public function getInstance() { if (!$this->isAvailable()) { throw new \Exception('Redis support is not available'); } if (!$this->instance instanceof \Redis) { $this->create(); } return $this->instance; } public function isAvailable() { return extension_loaded('redis') && version_compare(phpversion('redis'), '2.2.5', '>='); } } private/NotSquareException.php 0000604 00000001545 15247130451 0012532 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christopher Schäpers <kondou@ts.unde.re> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class NotSquareException extends \Exception { } private/Notification/Action.php 0000604 00000007134 15247130451 0012575 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Notification; use OCP\Notification\IAction; class Action implements IAction { /** @var string */ protected $label; /** @var string */ protected $labelParsed; /** @var string */ protected $link; /** @var string */ protected $requestType; /** @var string */ protected $icon; /** @var bool */ protected $primary; /** * Constructor */ public function __construct() { $this->label = ''; $this->labelParsed = ''; $this->link = ''; $this->requestType = ''; $this->primary = false; } /** * @param string $label * @return $this * @throws \InvalidArgumentException if the label is invalid * @since 8.2.0 */ public function setLabel($label) { if (!is_string($label) || $label === '' || isset($label[32])) { throw new \InvalidArgumentException('The given label is invalid'); } $this->label = $label; return $this; } /** * @return string * @since 8.2.0 */ public function getLabel() { return $this->label; } /** * @param string $label * @return $this * @throws \InvalidArgumentException if the label is invalid * @since 8.2.0 */ public function setParsedLabel($label) { if (!is_string($label) || $label === '') { throw new \InvalidArgumentException('The given parsed label is invalid'); } $this->labelParsed = $label; return $this; } /** * @return string * @since 8.2.0 */ public function getParsedLabel() { return $this->labelParsed; } /** * @param $primary bool * @return $this * @throws \InvalidArgumentException if $primary is invalid * @since 9.0.0 */ public function setPrimary($primary) { if (!is_bool($primary)) { throw new \InvalidArgumentException('The given primary option is invalid'); } $this->primary = $primary; return $this; } /** * @return bool * @since 9.0.0 */ public function isPrimary() { return $this->primary; } /** * @param string $link * @param string $requestType * @return $this * @throws \InvalidArgumentException if the link is invalid * @since 8.2.0 */ public function setLink($link, $requestType) { if (!is_string($link) || $link === '' || isset($link[256])) { throw new \InvalidArgumentException('The given link is invalid'); } if (!in_array($requestType, ['GET', 'POST', 'PUT', 'DELETE'], true)) { throw new \InvalidArgumentException('The given request type is invalid'); } $this->link = $link; $this->requestType = $requestType; return $this; } /** * @return string * @since 8.2.0 */ public function getLink() { return $this->link; } /** * @return string * @since 8.2.0 */ public function getRequestType() { return $this->requestType; } /** * @return bool */ public function isValid() { return $this->label !== '' && $this->link !== ''; } /** * @return bool */ public function isValidParsed() { return $this->labelParsed !== '' && $this->link !== ''; } } private/Notification/Notification.php 0000604 00000030274 15247130451 0014007 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Notification; use OCP\Notification\IAction; use OCP\Notification\INotification; use OCP\RichObjectStrings\InvalidObjectExeption; use OCP\RichObjectStrings\IValidator; class Notification implements INotification { /** @var IValidator */ protected $richValidator; /** @var string */ protected $app; /** @var string */ protected $user; /** @var \DateTime */ protected $dateTime; /** @var string */ protected $objectType; /** @var string */ protected $objectId; /** @var string */ protected $subject; /** @var array */ protected $subjectParameters; /** @var string */ protected $subjectParsed; /** @var string */ protected $subjectRich; /** @var array */ protected $subjectRichParameters; /** @var string */ protected $message; /** @var array */ protected $messageParameters; /** @var string */ protected $messageParsed; /** @var string */ protected $messageRich; /** @var array */ protected $messageRichParameters; /** @var string */ protected $link; /** @var string */ protected $icon; /** @var array */ protected $actions; /** @var array */ protected $actionsParsed; /** @var bool */ protected $hasPrimaryAction; /** @var bool */ protected $hasPrimaryParsedAction; /** * Constructor * * @param IValidator $richValidator */ public function __construct(IValidator $richValidator) { $this->richValidator = $richValidator; $this->app = ''; $this->user = ''; $this->dateTime = new \DateTime(); $this->dateTime->setTimestamp(0); $this->objectType = ''; $this->objectId = ''; $this->subject = ''; $this->subjectParameters = []; $this->subjectParsed = ''; $this->subjectRich = ''; $this->subjectRichParameters = []; $this->message = ''; $this->messageParameters = []; $this->messageParsed = ''; $this->messageRich = ''; $this->messageRichParameters = []; $this->link = ''; $this->icon = ''; $this->actions = []; $this->actionsParsed = []; } /** * @param string $app * @return $this * @throws \InvalidArgumentException if the app id is invalid * @since 8.2.0 */ public function setApp($app) { if (!is_string($app) || $app === '' || isset($app[32])) { throw new \InvalidArgumentException('The given app name is invalid'); } $this->app = $app; return $this; } /** * @return string * @since 8.2.0 */ public function getApp() { return $this->app; } /** * @param string $user * @return $this * @throws \InvalidArgumentException if the user id is invalid * @since 8.2.0 */ public function setUser($user) { if (!is_string($user) || $user === '' || isset($user[64])) { throw new \InvalidArgumentException('The given user id is invalid'); } $this->user = $user; return $this; } /** * @return string * @since 8.2.0 */ public function getUser() { return $this->user; } /** * @param \DateTime $dateTime * @return $this * @throws \InvalidArgumentException if the $dateTime is invalid * @since 9.0.0 */ public function setDateTime(\DateTime $dateTime) { if ($dateTime->getTimestamp() === 0) { throw new \InvalidArgumentException('The given date time is invalid'); } $this->dateTime = $dateTime; return $this; } /** * @return \DateTime * @since 9.0.0 */ public function getDateTime() { return $this->dateTime; } /** * @param string $type * @param string $id * @return $this * @throws \InvalidArgumentException if the object type or id is invalid * @since 8.2.0 - 9.0.0: Type of $id changed to string */ public function setObject($type, $id) { if (!is_string($type) || $type === '' || isset($type[64])) { throw new \InvalidArgumentException('The given object type is invalid'); } $this->objectType = $type; if (!is_int($id) && (!is_string($id) || $id === '' || isset($id[64]))) { throw new \InvalidArgumentException('The given object id is invalid'); } $this->objectId = (string) $id; return $this; } /** * @return string * @since 8.2.0 */ public function getObjectType() { return $this->objectType; } /** * @return string * @since 8.2.0 - 9.0.0: Return type changed to string */ public function getObjectId() { return $this->objectId; } /** * @param string $subject * @param array $parameters * @return $this * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 8.2.0 */ public function setSubject($subject, array $parameters = []) { if (!is_string($subject) || $subject === '' || isset($subject[64])) { throw new \InvalidArgumentException('The given subject is invalid'); } $this->subject = $subject; $this->subjectParameters = $parameters; return $this; } /** * @return string * @since 8.2.0 */ public function getSubject() { return $this->subject; } /** * @return string[] * @since 8.2.0 */ public function getSubjectParameters() { return $this->subjectParameters; } /** * @param string $subject * @return $this * @throws \InvalidArgumentException if the subject is invalid * @since 8.2.0 */ public function setParsedSubject($subject) { if (!is_string($subject) || $subject === '') { throw new \InvalidArgumentException('The given parsed subject is invalid'); } $this->subjectParsed = $subject; return $this; } /** * @return string * @since 8.2.0 */ public function getParsedSubject() { return $this->subjectParsed; } /** * @param string $subject * @param array $parameters * @return $this * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 11.0.0 */ public function setRichSubject($subject, array $parameters = []) { if (!is_string($subject) || $subject === '') { throw new \InvalidArgumentException('The given parsed subject is invalid'); } $this->subjectRich = $subject; $this->subjectRichParameters = $parameters; return $this; } /** * @return string * @since 11.0.0 */ public function getRichSubject() { return $this->subjectRich; } /** * @return array[] * @since 11.0.0 */ public function getRichSubjectParameters() { return $this->subjectRichParameters; } /** * @param string $message * @param array $parameters * @return $this * @throws \InvalidArgumentException if the message or parameters are invalid * @since 8.2.0 */ public function setMessage($message, array $parameters = []) { if (!is_string($message) || $message === '' || isset($message[64])) { throw new \InvalidArgumentException('The given message is invalid'); } $this->message = $message; $this->messageParameters = $parameters; return $this; } /** * @return string * @since 8.2.0 */ public function getMessage() { return $this->message; } /** * @return string[] * @since 8.2.0 */ public function getMessageParameters() { return $this->messageParameters; } /** * @param string $message * @return $this * @throws \InvalidArgumentException if the message is invalid * @since 8.2.0 */ public function setParsedMessage($message) { if (!is_string($message) || $message === '') { throw new \InvalidArgumentException('The given parsed message is invalid'); } $this->messageParsed = $message; return $this; } /** * @return string * @since 8.2.0 */ public function getParsedMessage() { return $this->messageParsed; } /** * @param string $message * @param array $parameters * @return $this * @throws \InvalidArgumentException if the message or parameters are invalid * @since 11.0.0 */ public function setRichMessage($message, array $parameters = []) { if (!is_string($message) || $message === '') { throw new \InvalidArgumentException('The given parsed message is invalid'); } $this->messageRich = $message; $this->messageRichParameters = $parameters; return $this; } /** * @return string * @since 11.0.0 */ public function getRichMessage() { return $this->messageRich; } /** * @return array[] * @since 11.0.0 */ public function getRichMessageParameters() { return $this->messageRichParameters; } /** * @param string $link * @return $this * @throws \InvalidArgumentException if the link is invalid * @since 8.2.0 */ public function setLink($link) { if (!is_string($link) || $link === '' || isset($link[4000])) { throw new \InvalidArgumentException('The given link is invalid'); } $this->link = $link; return $this; } /** * @return string * @since 8.2.0 */ public function getLink() { return $this->link; } /** * @param string $icon * @return $this * @throws \InvalidArgumentException if the icon is invalid * @since 11.0.0 */ public function setIcon($icon) { if (!is_string($icon) || $icon === '' || isset($icon[4000])) { throw new \InvalidArgumentException('The given icon is invalid'); } $this->icon = $icon; return $this; } /** * @return string * @since 11.0.0 */ public function getIcon() { return $this->icon; } /** * @return IAction * @since 8.2.0 */ public function createAction() { return new Action(); } /** * @param IAction $action * @return $this * @throws \InvalidArgumentException if the action is invalid * @since 8.2.0 */ public function addAction(IAction $action) { if (!$action->isValid()) { throw new \InvalidArgumentException('The given action is invalid'); } if ($action->isPrimary()) { if ($this->hasPrimaryAction) { throw new \InvalidArgumentException('The notification already has a primary action'); } $this->hasPrimaryAction = true; } $this->actions[] = $action; return $this; } /** * @return IAction[] * @since 8.2.0 */ public function getActions() { return $this->actions; } /** * @param IAction $action * @return $this * @throws \InvalidArgumentException if the action is invalid * @since 8.2.0 */ public function addParsedAction(IAction $action) { if (!$action->isValidParsed()) { throw new \InvalidArgumentException('The given parsed action is invalid'); } if ($action->isPrimary()) { if ($this->hasPrimaryParsedAction) { throw new \InvalidArgumentException('The notification already has a primary action'); } $this->hasPrimaryParsedAction = true; // Make sure the primary action is always the first one array_unshift($this->actionsParsed, $action); } else { $this->actionsParsed[] = $action; } return $this; } /** * @return IAction[] * @since 8.2.0 */ public function getParsedActions() { return $this->actionsParsed; } /** * @return bool * @since 8.2.0 */ public function isValid() { return $this->isValidCommon() && $this->getSubject() !== '' ; } /** * @return bool * @since 8.2.0 */ public function isValidParsed() { if ($this->getRichSubject() !== '' || !empty($this->getRichSubjectParameters())) { try { $this->richValidator->validate($this->getRichSubject(), $this->getRichSubjectParameters()); } catch (InvalidObjectExeption $e) { return false; } } if ($this->getRichMessage() !== '' || !empty($this->getRichMessageParameters())) { try { $this->richValidator->validate($this->getRichMessage(), $this->getRichMessageParameters()); } catch (InvalidObjectExeption $e) { return false; } } return $this->isValidCommon() && $this->getParsedSubject() !== '' ; } /** * @return bool */ protected function isValidCommon() { return $this->getApp() !== '' && $this->getUser() !== '' && $this->getDateTime()->getTimestamp() !== 0 && $this->getObjectType() !== '' && $this->getObjectId() !== '' ; } } private/Notification/Manager.php 0000604 00000014314 15247130451 0012730 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Notification; use OCP\Notification\IApp; use OCP\Notification\IManager; use OCP\Notification\INotification; use OCP\Notification\INotifier; use OCP\RichObjectStrings\IValidator; class Manager implements IManager { /** @var IValidator */ protected $validator; /** @var IApp[] */ protected $apps; /** @var INotifier[] */ protected $notifiers; /** @var array[] */ protected $notifiersInfo; /** @var \Closure[] */ protected $appsClosures; /** @var \Closure[] */ protected $notifiersClosures; /** @var \Closure[] */ protected $notifiersInfoClosures; /** * Manager constructor. * * @param IValidator $validator */ public function __construct(IValidator $validator) { $this->validator = $validator; $this->apps = []; $this->notifiers = []; $this->notifiersInfo = []; $this->appsClosures = []; $this->notifiersClosures = []; $this->notifiersInfoClosures = []; } /** * @param \Closure $service The service must implement IApp, otherwise a * \InvalidArgumentException is thrown later * @since 8.2.0 */ public function registerApp(\Closure $service) { $this->appsClosures[] = $service; $this->apps = []; } /** * @param \Closure $service The service must implement INotifier, otherwise a * \InvalidArgumentException is thrown later * @param \Closure $info An array with the keys 'id' and 'name' containing * the app id and the app name * @since 8.2.0 - Parameter $info was added in 9.0.0 */ public function registerNotifier(\Closure $service, \Closure $info) { $this->notifiersClosures[] = $service; $this->notifiersInfoClosures[] = $info; $this->notifiers = []; $this->notifiersInfo = []; } /** * @return IApp[] */ protected function getApps() { if (!empty($this->apps)) { return $this->apps; } $this->apps = []; foreach ($this->appsClosures as $closure) { $app = $closure(); if (!($app instanceof IApp)) { throw new \InvalidArgumentException('The given notification app does not implement the IApp interface'); } $this->apps[] = $app; } return $this->apps; } /** * @return INotifier[] */ protected function getNotifiers() { if (!empty($this->notifiers)) { return $this->notifiers; } $this->notifiers = []; foreach ($this->notifiersClosures as $closure) { $notifier = $closure(); if (!($notifier instanceof INotifier)) { throw new \InvalidArgumentException('The given notifier does not implement the INotifier interface'); } $this->notifiers[] = $notifier; } return $this->notifiers; } /** * @return array[] */ public function listNotifiers() { if (!empty($this->notifiersInfo)) { return $this->notifiersInfo; } $this->notifiersInfo = []; foreach ($this->notifiersInfoClosures as $closure) { $notifier = $closure(); if (!is_array($notifier) || sizeof($notifier) !== 2 || !isset($notifier['id']) || !isset($notifier['name'])) { throw new \InvalidArgumentException('The given notifier information is invalid'); } if (isset($this->notifiersInfo[$notifier['id']])) { throw new \InvalidArgumentException('The given notifier ID ' . $notifier['id'] . ' is already in use'); } $this->notifiersInfo[$notifier['id']] = $notifier['name']; } return $this->notifiersInfo; } /** * @return INotification * @since 8.2.0 */ public function createNotification() { return new Notification($this->validator); } /** * @return bool * @since 8.2.0 */ public function hasNotifiers() { return !empty($this->notifiersClosures); } /** * @param INotification $notification * @throws \InvalidArgumentException When the notification is not valid * @since 8.2.0 */ public function notify(INotification $notification) { if (!$notification->isValid()) { throw new \InvalidArgumentException('The given notification is invalid'); } $apps = $this->getApps(); foreach ($apps as $app) { try { $app->notify($notification); } catch (\InvalidArgumentException $e) { } } } /** * @param INotification $notification * @param string $languageCode The code of the language that should be used to prepare the notification * @return INotification * @throws \InvalidArgumentException When the notification was not prepared by a notifier * @since 8.2.0 */ public function prepare(INotification $notification, $languageCode) { $notifiers = $this->getNotifiers(); foreach ($notifiers as $notifier) { try { $notification = $notifier->prepare($notification, $languageCode); } catch (\InvalidArgumentException $e) { continue; } if (!($notification instanceof INotification) || !$notification->isValidParsed()) { throw new \InvalidArgumentException('The given notification has not been handled'); } } if (!($notification instanceof INotification) || !$notification->isValidParsed()) { throw new \InvalidArgumentException('The given notification has not been handled'); } return $notification; } /** * @param INotification $notification */ public function markProcessed(INotification $notification) { $apps = $this->getApps(); foreach ($apps as $app) { $app->markProcessed($notification); } } /** * @param INotification $notification * @return int */ public function getCount(INotification $notification) { $apps = $this->getApps(); $count = 0; foreach ($apps as $app) { $count += $app->getCount($notification); } return $count; } } private/URLGenerator.php 0000604 00000022526 15247130451 0011245 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Felix Anand Epp <work@felixepp.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author mmccarn <mmccarn-github@mmsionline.us> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IRequest; use OCP\IURLGenerator; use OCP\Route\IRoute; use RuntimeException; /** * Class to generate URLs */ class URLGenerator implements IURLGenerator { /** @var IConfig */ private $config; /** @var ICacheFactory */ private $cacheFactory; /** @var IRequest */ private $request; /** * @param IConfig $config * @param ICacheFactory $cacheFactory * @param IRequest $request */ public function __construct(IConfig $config, ICacheFactory $cacheFactory, IRequest $request) { $this->config = $config; $this->cacheFactory = $cacheFactory; $this->request = $request; } /** * Creates an url using a defined route * @param string $route * @param array $parameters args with param=>value, will be appended to the returned url * @return string the url * * Returns a url to the given route. */ public function linkToRoute($route, $parameters = array()) { // TODO: mock router $urlLinkTo = \OC::$server->getRouter()->generate($route, $parameters); return $urlLinkTo; } /** * Creates an absolute url using a defined route * @param string $routeName * @param array $arguments args with param=>value, will be appended to the returned url * @return string the url * * Returns an absolute url to the given route. */ public function linkToRouteAbsolute($routeName, $arguments = array()) { return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments)); } /** * Creates an url * @param string $app app * @param string $file file * @param array $args array with param=>value, will be appended to the returned url * The value of $args will be urlencoded * @return string the url * * Returns a url to the given app and file. */ public function linkTo( $app, $file, $args = array() ) { $frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'); if( $app != '' ) { $app_path = \OC_App::getAppPath($app); // Check if the app is in the app folder if ($app_path && file_exists($app_path . '/' . $file)) { if (substr($file, -3) == 'php') { $urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $app; if ($frontControllerActive) { $urlLinkTo = \OC::$WEBROOT . '/apps/' . $app; } $urlLinkTo .= ($file != 'index.php') ? '/' . $file : ''; } else { $urlLinkTo = \OC_App::getAppWebPath($app) . '/' . $file; } } else { $urlLinkTo = \OC::$WEBROOT . '/' . $app . '/' . $file; } } else { if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) { $urlLinkTo = \OC::$WEBROOT . '/core/' . $file; } else { if ($frontControllerActive && $file === 'index.php') { $urlLinkTo = \OC::$WEBROOT . '/'; } else { $urlLinkTo = \OC::$WEBROOT . '/' . $file; } } } if ($args && $query = http_build_query($args, '', '&')) { $urlLinkTo .= '?' . $query; } return $urlLinkTo; } /** * Creates path to an image * @param string $app app * @param string $image image name * @throws \RuntimeException If the image does not exist * @return string the url * * Returns the path to the image. */ public function imagePath($app, $image) { $cache = $this->cacheFactory->create('imagePath-'.md5($this->getBaseUrl()).'-'); $cacheKey = $app.'-'.$image; if($key = $cache->get($cacheKey)) { return $key; } // Read the selected theme from the config file $theme = \OC_Util::getTheme(); //if a theme has a png but not an svg always use the png $basename = substr(basename($image),0,-4); $appPath = \OC_App::getAppPath($app); // Check if the app is in the app folder $path = ''; $themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming'); if($themingEnabled && $image === 'favicon.ico' && \OC::$server->getThemingDefaults()->shouldReplaceIcons()) { $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); if($app === '') { $app = 'core'; } $path = $this->linkToRoute('theming.Icon.getFavicon', [ 'app' => $app ]) . '?v='. $cacheBusterValue; } elseif($themingEnabled && $image === 'favicon-touch.png' && \OC::$server->getThemingDefaults()->shouldReplaceIcons()) { $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); if($app === '') { $app = 'core'; } $path = $this->linkToRoute('theming.Icon.getTouchIcon', [ 'app' => $app ]) . '?v='. $cacheBusterValue; } elseif($themingEnabled && $image === 'favicon-fb.png' && \OC::$server->getThemingDefaults()->shouldReplaceIcons()) { $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); if($app === '') { $app = 'core'; } $path = $this->linkToRoute('theming.Icon.getTouchIcon', [ 'app' => $app ]) . '?v='. $cacheBusterValue; } elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$image")) { $path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$image"; } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.svg") && file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.png")) { $path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$basename.png"; } elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$image")) { $path = \OC::$WEBROOT . "/themes/$theme/$app/img/$image"; } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.svg") && file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.png"))) { $path = \OC::$WEBROOT . "/themes/$theme/$app/img/$basename.png"; } elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$image")) { $path = \OC::$WEBROOT . "/themes/$theme/core/img/$image"; } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg") && file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) { $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png"; } elseif ($appPath && file_exists($appPath . "/img/$image")) { $path = \OC_App::getAppWebPath($app) . "/img/$image"; } elseif ($appPath && !file_exists($appPath . "/img/$basename.svg") && file_exists($appPath . "/img/$basename.png")) { $path = \OC_App::getAppWebPath($app) . "/img/$basename.png"; } elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/$app/img/$image")) { $path = \OC::$WEBROOT . "/$app/img/$image"; } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/$app/img/$basename.svg") && file_exists(\OC::$SERVERROOT . "/$app/img/$basename.png"))) { $path = \OC::$WEBROOT . "/$app/img/$basename.png"; } elseif (file_exists(\OC::$SERVERROOT . "/core/img/$image")) { $path = \OC::$WEBROOT . "/core/img/$image"; } elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg") && file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) { $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png"; } if($path !== '') { $cache->set($cacheKey, $path); return $path; } else { throw new RuntimeException('image not found: image:' . $image . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT); } } /** * Makes an URL absolute * @param string $url the url in the ownCloud host * @return string the absolute version of the url */ public function getAbsoluteURL($url) { $separator = $url[0] === '/' ? '' : '/'; if (\OC::$CLI && !defined('PHPUNIT_RUN')) { return rtrim($this->config->getSystemValue('overwrite.cli.url'), '/') . '/' . ltrim($url, '/'); } // The ownCloud web root can already be prepended. if(substr($url, 0, strlen(\OC::$WEBROOT)) === \OC::$WEBROOT) { $url = substr($url, strlen(\OC::$WEBROOT)); } return $this->getBaseUrl() . $separator . $url; } /** * @param string $key * @return string url to the online documentation */ public function linkToDocs($key) { $theme = \OC::$server->getThemingDefaults(); return $theme->buildDocLinkToKey($key); } /** * @return string base url of the current request */ public function getBaseUrl() { return $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT; } } private/Activity/Event.php 0000604 00000027745 15247130451 0011621 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * @author Phil Davis <phil.davis@inf.org> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Activity; use OCP\Activity\IEvent; use OCP\RichObjectStrings\InvalidObjectExeption; use OCP\RichObjectStrings\IValidator; class Event implements IEvent { /** @var string */ protected $app = ''; /** @var string */ protected $type = ''; /** @var string */ protected $affectedUser = ''; /** @var string */ protected $author = ''; /** @var int */ protected $timestamp = 0; /** @var string */ protected $subject = ''; /** @var array */ protected $subjectParameters = []; /** @var string */ protected $subjectParsed; /** @var string */ protected $subjectRich; /** @var array */ protected $subjectRichParameters; /** @var string */ protected $message = ''; /** @var array */ protected $messageParameters = []; /** @var string */ protected $messageParsed; /** @var string */ protected $messageRich; /** @var array */ protected $messageRichParameters; /** @var string */ protected $objectType = ''; /** @var int */ protected $objectId = 0; /** @var string */ protected $objectName = ''; /** @var string */ protected $link = ''; /** @var string */ protected $icon = ''; /** @var IEvent */ protected $child = null; /** @var IValidator */ protected $richValidator; /** * @param IValidator $richValidator */ public function __construct(IValidator $richValidator) { $this->richValidator = $richValidator; } /** * Set the app of the activity * * @param string $app * @return IEvent * @throws \InvalidArgumentException if the app id is invalid * @since 8.2.0 */ public function setApp($app) { if (!is_string($app) || $app === '' || isset($app[32])) { throw new \InvalidArgumentException('The given app is invalid'); } $this->app = (string) $app; return $this; } /** * @return string */ public function getApp() { return $this->app; } /** * Set the type of the activity * * @param string $type * @return IEvent * @throws \InvalidArgumentException if the type is invalid * @since 8.2.0 */ public function setType($type) { if (!is_string($type) || $type === '' || isset($type[255])) { throw new \InvalidArgumentException('The given type is invalid'); } $this->type = (string) $type; return $this; } /** * @return string */ public function getType() { return $this->type; } /** * Set the affected user of the activity * * @param string $affectedUser * @return IEvent * @throws \InvalidArgumentException if the affected user is invalid * @since 8.2.0 */ public function setAffectedUser($affectedUser) { if (!is_string($affectedUser) || $affectedUser === '' || isset($affectedUser[64])) { throw new \InvalidArgumentException('The given affected user is invalid'); } $this->affectedUser = (string) $affectedUser; return $this; } /** * @return string */ public function getAffectedUser() { return $this->affectedUser; } /** * Set the author of the activity * * @param string $author * @return IEvent * @throws \InvalidArgumentException if the author is invalid * @since 8.2.0 */ public function setAuthor($author) { if (!is_string($author) || isset($author[64])) { throw new \InvalidArgumentException('The given author user is invalid'. serialize($author)); } $this->author = (string) $author; return $this; } /** * @return string */ public function getAuthor() { return $this->author; } /** * Set the timestamp of the activity * * @param int $timestamp * @return IEvent * @throws \InvalidArgumentException if the timestamp is invalid * @since 8.2.0 */ public function setTimestamp($timestamp) { if (!is_int($timestamp)) { throw new \InvalidArgumentException('The given timestamp is invalid'); } $this->timestamp = (int) $timestamp; return $this; } /** * @return int */ public function getTimestamp() { return $this->timestamp; } /** * Set the subject of the activity * * @param string $subject * @param array $parameters * @return IEvent * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 8.2.0 */ public function setSubject($subject, array $parameters = []) { if (!is_string($subject) || isset($subject[255])) { throw new \InvalidArgumentException('The given subject is invalid'); } $this->subject = (string) $subject; $this->subjectParameters = $parameters; return $this; } /** * @return string */ public function getSubject() { return $this->subject; } /** * @return array */ public function getSubjectParameters() { return $this->subjectParameters; } /** * @param string $subject * @return $this * @throws \InvalidArgumentException if the subject is invalid * @since 11.0.0 */ public function setParsedSubject($subject) { if (!is_string($subject) || $subject === '') { throw new \InvalidArgumentException('The given parsed subject is invalid'); } $this->subjectParsed = $subject; return $this; } /** * @return string * @since 11.0.0 */ public function getParsedSubject() { return $this->subjectParsed; } /** * @param string $subject * @param array $parameters * @return $this * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 11.0.0 */ public function setRichSubject($subject, array $parameters = []) { if (!is_string($subject) || $subject === '') { throw new \InvalidArgumentException('The given parsed subject is invalid'); } $this->subjectRich = $subject; if (!is_array($parameters)) { throw new \InvalidArgumentException('The given subject parameters are invalid'); } $this->subjectRichParameters = $parameters; return $this; } /** * @return string * @since 11.0.0 */ public function getRichSubject() { return $this->subjectRich; } /** * @return array[] * @since 11.0.0 */ public function getRichSubjectParameters() { return $this->subjectRichParameters; } /** * Set the message of the activity * * @param string $message * @param array $parameters * @return IEvent * @throws \InvalidArgumentException if the message or parameters are invalid * @since 8.2.0 */ public function setMessage($message, array $parameters = []) { if (!is_string($message) || isset($message[255])) { throw new \InvalidArgumentException('The given message is invalid'); } $this->message = (string) $message; $this->messageParameters = $parameters; return $this; } /** * @return string */ public function getMessage() { return $this->message; } /** * @return array */ public function getMessageParameters() { return $this->messageParameters; } /** * @param string $message * @return $this * @throws \InvalidArgumentException if the message is invalid * @since 11.0.0 */ public function setParsedMessage($message) { if (!is_string($message)) { throw new \InvalidArgumentException('The given parsed message is invalid'); } $this->messageParsed = $message; return $this; } /** * @return string * @since 11.0.0 */ public function getParsedMessage() { return $this->messageParsed; } /** * @param string $message * @param array $parameters * @return $this * @throws \InvalidArgumentException if the subject or parameters are invalid * @since 11.0.0 */ public function setRichMessage($message, array $parameters = []) { if (!is_string($message)) { throw new \InvalidArgumentException('The given parsed message is invalid'); } $this->messageRich = $message; if (!is_array($parameters)) { throw new \InvalidArgumentException('The given message parameters are invalid'); } $this->messageRichParameters = $parameters; return $this; } /** * @return string * @since 11.0.0 */ public function getRichMessage() { return $this->messageRich; } /** * @return array[] * @since 11.0.0 */ public function getRichMessageParameters() { return $this->messageRichParameters; } /** * Set the object of the activity * * @param string $objectType * @param int $objectId * @param string $objectName * @return IEvent * @throws \InvalidArgumentException if the object is invalid * @since 8.2.0 */ public function setObject($objectType, $objectId, $objectName = '') { if (!is_string($objectType) || isset($objectType[255])) { throw new \InvalidArgumentException('The given object type is invalid'); } if (!is_int($objectId)) { throw new \InvalidArgumentException('The given object id is invalid'); } if (!is_string($objectName) || isset($objectName[4000])) { throw new \InvalidArgumentException('The given object name is invalid'); } $this->objectType = (string) $objectType; $this->objectId = (int) $objectId; $this->objectName = (string) $objectName; return $this; } /** * @return string */ public function getObjectType() { return $this->objectType; } /** * @return string */ public function getObjectId() { return $this->objectId; } /** * @return string */ public function getObjectName() { return $this->objectName; } /** * Set the link of the activity * * @param string $link * @return IEvent * @throws \InvalidArgumentException if the link is invalid * @since 8.2.0 */ public function setLink($link) { if (!is_string($link) || isset($link[4000])) { throw new \InvalidArgumentException('The given link is invalid'); } $this->link = (string) $link; return $this; } /** * @return string */ public function getLink() { return $this->link; } /** * @param string $icon * @return $this * @throws \InvalidArgumentException if the icon is invalid * @since 11.0.0 */ public function setIcon($icon) { if (!is_string($icon) || isset($icon[4000])) { throw new \InvalidArgumentException('The given icon is invalid'); } $this->icon = $icon; return $this; } /** * @return string * @since 11.0.0 */ public function getIcon() { return $this->icon; } /** * @param IEvent $child * @since 11.0.0 */ public function setChildEvent(IEvent $child) { $this->child = $child; } /** * @return IEvent|null * @since 11.0.0 */ public function getChildEvent() { return $this->child; } /** * @return bool * @since 8.2.0 */ public function isValid() { return $this->isValidCommon() && $this->getSubject() !== '' ; } /** * @return bool * @since 8.2.0 */ public function isValidParsed() { if ($this->getRichSubject() !== '' || !empty($this->getRichSubjectParameters())) { try { $this->richValidator->validate($this->getRichSubject(), $this->getRichSubjectParameters()); } catch (InvalidObjectExeption $e) { return false; } } if ($this->getRichMessage() !== '' || !empty($this->getRichMessageParameters())) { try { $this->richValidator->validate($this->getRichMessage(), $this->getRichMessageParameters()); } catch (InvalidObjectExeption $e) { return false; } } return $this->isValidCommon() && $this->getParsedSubject() !== '' ; } /** * @return bool */ protected function isValidCommon() { return $this->getApp() !== '' && $this->getType() !== '' && $this->getAffectedUser() !== '' && $this->getTimestamp() !== 0 /** * Disabled for BC with old activities && $this->getObjectType() !== '' && $this->getObjectId() !== 0 */ ; } } private/Activity/LegacySetting.php 0000604 00000006113 15247130451 0013264 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Activity; use OCP\Activity\ISetting; class LegacySetting implements ISetting { /** @var string */ protected $identifier; /** @var string */ protected $name; /** @var bool */ protected $canChangeStream; /** @var bool */ protected $isDefaultEnabledStream; /** @var bool */ protected $canChangeMail; /** @var bool */ protected $isDefaultEnabledMail; /** * LegacySetting constructor. * * @param string $identifier * @param string $name * @param bool $canChangeStream * @param bool $isDefaultEnabledStream * @param bool $canChangeMail * @param bool $isDefaultEnabledMail */ public function __construct($identifier, $name, $canChangeStream, $isDefaultEnabledStream, $canChangeMail, $isDefaultEnabledMail) { $this->identifier = $identifier; $this->name = $name; $this->canChangeStream = $canChangeStream; $this->isDefaultEnabledStream = $isDefaultEnabledStream; $this->canChangeMail = $canChangeMail; $this->isDefaultEnabledMail = $isDefaultEnabledMail; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return $this->identifier; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->name; } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 70; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return $this->canChangeStream; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return $this->isDefaultEnabledStream; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return $this->canChangeMail; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return $this->isDefaultEnabledMail; } } private/Activity/Manager.php 0000604 00000044164 15247130451 0012104 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Activity; use OCP\Activity\IConsumer; use OCP\Activity\IEvent; use OCP\Activity\IExtension; use OCP\Activity\IFilter; use OCP\Activity\IManager; use OCP\Activity\IProvider; use OCP\Activity\ISetting; use OCP\IConfig; use OCP\IRequest; use OCP\IUser; use OCP\IUserSession; use OCP\RichObjectStrings\IValidator; class Manager implements IManager { /** @var IRequest */ protected $request; /** @var IUserSession */ protected $session; /** @var IConfig */ protected $config; /** @var IValidator */ protected $validator; /** @var string */ protected $formattingObjectType; /** @var int */ protected $formattingObjectId; /** @var bool */ protected $requirePNG; /** @var string */ protected $currentUserId; /** * constructor of the controller * * @param IRequest $request * @param IUserSession $session * @param IConfig $config * @param IValidator $validator */ public function __construct(IRequest $request, IUserSession $session, IConfig $config, IValidator $validator) { $this->request = $request; $this->session = $session; $this->config = $config; $this->validator = $validator; } /** @var \Closure[] */ private $consumersClosures = array(); /** @var IConsumer[] */ private $consumers = array(); /** @var \Closure[] */ private $extensionsClosures = array(); /** @var IExtension[] */ private $extensions = array(); /** @var array list of filters "name" => "is valid" */ protected $validFilters = array( 'all' => true, 'by' => true, 'self' => true, ); /** @var array list of type icons "type" => "css class" */ protected $typeIcons = array(); /** @var array list of special parameters "app" => ["text" => ["parameter" => "type"]] */ protected $specialParameters = array(); /** * @return \OCP\Activity\IConsumer[] */ protected function getConsumers() { if (!empty($this->consumers)) { return $this->consumers; } $this->consumers = []; foreach($this->consumersClosures as $consumer) { $c = $consumer(); if ($c instanceof IConsumer) { $this->consumers[] = $c; } else { throw new \InvalidArgumentException('The given consumer does not implement the \OCP\Activity\IConsumer interface'); } } return $this->consumers; } /** * @return \OCP\Activity\IExtension[] */ protected function getExtensions() { if (!empty($this->extensions)) { return $this->extensions; } $this->extensions = []; foreach($this->extensionsClosures as $extension) { $e = $extension(); if ($e instanceof IExtension) { $this->extensions[] = $e; } else { throw new \InvalidArgumentException('The given extension does not implement the \OCP\Activity\IExtension interface'); } } return $this->extensions; } /** * Generates a new IEvent object * * Make sure to call at least the following methods before sending it to the * app with via the publish() method: * - setApp() * - setType() * - setAffectedUser() * - setSubject() * * @return IEvent */ public function generateEvent() { return new Event($this->validator); } /** * Publish an event to the activity consumers * * Make sure to call at least the following methods before sending an Event: * - setApp() * - setType() * - setAffectedUser() * - setSubject() * * @param IEvent $event * @throws \BadMethodCallException if required values have not been set */ public function publish(IEvent $event) { if ($event->getAuthor() === '') { if ($this->session->getUser() instanceof IUser) { $event->setAuthor($this->session->getUser()->getUID()); } } if (!$event->getTimestamp()) { $event->setTimestamp(time()); } if (!$event->isValid()) { throw new \BadMethodCallException('The given event is invalid'); } foreach ($this->getConsumers() as $c) { $c->receive($event); } } /** * @param string $app The app where this event is associated with * @param string $subject A short description of the event * @param array $subjectParams Array with parameters that are filled in the subject * @param string $message A longer description of the event * @param array $messageParams Array with parameters that are filled in the message * @param string $file The file including path where this event is associated with * @param string $link A link where this event is associated with * @param string $affectedUser Recipient of the activity * @param string $type Type of the notification * @param int $priority Priority of the notification */ public function publishActivity($app, $subject, $subjectParams, $message, $messageParams, $file, $link, $affectedUser, $type, $priority) { $event = $this->generateEvent(); $event->setApp($app) ->setType($type) ->setAffectedUser($affectedUser) ->setSubject($subject, $subjectParams) ->setMessage($message, $messageParams) ->setObject('', 0, $file) ->setLink($link); $this->publish($event); } /** * In order to improve lazy loading a closure can be registered which will be called in case * activity consumers are actually requested * * $callable has to return an instance of OCA\Activity\IConsumer * * @param \Closure $callable */ public function registerConsumer(\Closure $callable) { array_push($this->consumersClosures, $callable); $this->consumers = []; } /** * In order to improve lazy loading a closure can be registered which will be called in case * activity consumers are actually requested * * $callable has to return an instance of OCA\Activity\IExtension * * @param \Closure $callable */ public function registerExtension(\Closure $callable) { array_push($this->extensionsClosures, $callable); $this->extensions = []; } /** @var string[] */ protected $filterClasses = []; /** @var IFilter[] */ protected $filters = []; /** @var bool */ protected $loadedLegacyFilters = false; /** * @param string $filter Class must implement OCA\Activity\IFilter * @return void */ public function registerFilter($filter) { $this->filterClasses[$filter] = false; } /** * @return IFilter[] * @throws \InvalidArgumentException */ public function getFilters() { if (!$this->loadedLegacyFilters) { $legacyFilters = $this->getNavigation(); foreach ($legacyFilters['top'] as $filter => $data) { $this->filters[$filter] = new LegacyFilter( $this, $filter, $data['name'], true ); } foreach ($legacyFilters['apps'] as $filter => $data) { $this->filters[$filter] = new LegacyFilter( $this, $filter, $data['name'], false ); } $this->loadedLegacyFilters = true; } foreach ($this->filterClasses as $class => $false) { /** @var IFilter $filter */ $filter = \OC::$server->query($class); if (!$filter instanceof IFilter) { throw new \InvalidArgumentException('Invalid activity filter registered'); } $this->filters[$filter->getIdentifier()] = $filter; unset($this->filterClasses[$class]); } return $this->filters; } /** * @param string $id * @return IFilter * @throws \InvalidArgumentException when the filter was not found * @since 11.0.0 */ public function getFilterById($id) { $filters = $this->getFilters(); if (isset($filters[$id])) { return $filters[$id]; } throw new \InvalidArgumentException('Requested filter does not exist'); } /** @var string[] */ protected $providerClasses = []; /** @var IProvider[] */ protected $providers = []; /** * @param string $provider Class must implement OCA\Activity\IProvider * @return void */ public function registerProvider($provider) { $this->providerClasses[$provider] = false; } /** * @return IProvider[] * @throws \InvalidArgumentException */ public function getProviders() { foreach ($this->providerClasses as $class => $false) { /** @var IProvider $provider */ $provider = \OC::$server->query($class); if (!$provider instanceof IProvider) { throw new \InvalidArgumentException('Invalid activity provider registered'); } $this->providers[] = $provider; unset($this->providerClasses[$class]); } return $this->providers; } /** @var string[] */ protected $settingsClasses = []; /** @var ISetting[] */ protected $settings = []; /** @var bool */ protected $loadedLegacyTypes = false; /** * @param string $setting Class must implement OCA\Activity\ISetting * @return void */ public function registerSetting($setting) { $this->settingsClasses[$setting] = false; } /** * @return ISetting[] * @throws \InvalidArgumentException */ public function getSettings() { if (!$this->loadedLegacyTypes) { $l = \OC::$server->getL10N('core'); $legacyTypes = $this->getNotificationTypes($l->getLanguageCode()); $streamTypes = $this->getDefaultTypes(IExtension::METHOD_STREAM); $mailTypes = $this->getDefaultTypes(IExtension::METHOD_MAIL); foreach ($legacyTypes as $type => $data) { if (is_string($data)) { $desc = $data; $canChangeStream = true; $canChangeMail = true; } else { $desc = $data['desc']; $canChangeStream = in_array(IExtension::METHOD_STREAM, $data['methods']); $canChangeMail = in_array(IExtension::METHOD_MAIL, $data['methods']); } $this->settings[$type] = new LegacySetting( $type, $desc, $canChangeStream, in_array($type, $streamTypes), $canChangeMail, in_array($type, $mailTypes) ); } $this->loadedLegacyTypes = true; } foreach ($this->settingsClasses as $class => $false) { /** @var ISetting $setting */ $setting = \OC::$server->query($class); if (!$setting instanceof ISetting) { throw new \InvalidArgumentException('Invalid activity filter registered'); } $this->settings[$setting->getIdentifier()] = $setting; unset($this->settingsClasses[$class]); } return $this->settings; } /** * @param string $id * @return ISetting * @throws \InvalidArgumentException when the setting was not found * @since 11.0.0 */ public function getSettingById($id) { $settings = $this->getSettings(); if (isset($settings[$id])) { return $settings[$id]; } throw new \InvalidArgumentException('Requested setting does not exist'); } /** * @param string $type * @return string */ public function getTypeIcon($type) { if (isset($this->typeIcons[$type])) { return $this->typeIcons[$type]; } foreach ($this->getExtensions() as $c) { $icon = $c->getTypeIcon($type); if (is_string($icon)) { $this->typeIcons[$type] = $icon; return $icon; } } $this->typeIcons[$type] = ''; return ''; } /** * @param string $type * @param string $id */ public function setFormattingObject($type, $id) { $this->formattingObjectType = $type; $this->formattingObjectId = (string) $id; } /** * @return bool */ public function isFormattingFilteredObject() { return $this->formattingObjectType !== null && $this->formattingObjectId !== null && $this->formattingObjectType === $this->request->getParam('object_type') && $this->formattingObjectId === $this->request->getParam('object_id'); } /** * @param bool $status Set to true, when parsing events should not use SVG icons */ public function setRequirePNG($status) { $this->requirePNG = $status; } /** * @return bool */ public function getRequirePNG() { return $this->requirePNG; } /** * @param string $app * @param string $text * @param array $params * @param boolean $stripPath * @param boolean $highlightParams * @param string $languageCode * @return string|false */ public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) { foreach ($this->getExtensions() as $c) { $translation = $c->translate($app, $text, $params, $stripPath, $highlightParams, $languageCode); if (is_string($translation)) { return $translation; } } return false; } /** * @param string $app * @param string $text * @return array|false */ public function getSpecialParameterList($app, $text) { if (isset($this->specialParameters[$app][$text])) { return $this->specialParameters[$app][$text]; } if (!isset($this->specialParameters[$app])) { $this->specialParameters[$app] = array(); } foreach ($this->getExtensions() as $c) { $specialParameter = $c->getSpecialParameterList($app, $text); if (is_array($specialParameter)) { $this->specialParameters[$app][$text] = $specialParameter; return $specialParameter; } } $this->specialParameters[$app][$text] = false; return false; } /** * @param array $activity * @return integer|false */ public function getGroupParameter($activity) { foreach ($this->getExtensions() as $c) { $parameter = $c->getGroupParameter($activity); if ($parameter !== false) { return $parameter; } } return false; } /** * Set the user we need to use * * @param string|null $currentUserId * @throws \UnexpectedValueException If the user is invalid */ public function setCurrentUserId($currentUserId) { if (!is_string($currentUserId) && $currentUserId !== null) { throw new \UnexpectedValueException('The given current user is invalid'); } $this->currentUserId = $currentUserId; } /** * Get the user we need to use * * Either the user is logged in, or we try to get it from the token * * @return string * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique */ public function getCurrentUserId() { if ($this->currentUserId !== null) { return $this->currentUserId; } else if (!$this->session->isLoggedIn()) { return $this->getUserFromToken(); } else { return $this->session->getUser()->getUID(); } } /** * Get the user for the token * * @return string * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique */ protected function getUserFromToken() { $token = (string) $this->request->getParam('token', ''); if (strlen($token) !== 30) { throw new \UnexpectedValueException('The token is invalid'); } $users = $this->config->getUsersForUserValue('activity', 'rsstoken', $token); if (sizeof($users) !== 1) { // No unique user found throw new \UnexpectedValueException('The token is invalid'); } // Token found login as that user return array_shift($users); } /** * @return array * @deprecated 11.0.0 - Use getFilters() instead */ public function getNavigation() { $entries = array( 'apps' => array(), 'top' => array(), ); foreach ($this->getExtensions() as $c) { $additionalEntries = $c->getNavigation(); if (is_array($additionalEntries)) { $entries['apps'] = array_merge($entries['apps'], $additionalEntries['apps']); $entries['top'] = array_merge($entries['top'], $additionalEntries['top']); } } return $entries; } /** * @param string $filterValue * @return boolean * @deprecated 11.0.0 - Use getFilterById() instead */ public function isFilterValid($filterValue) { if (isset($this->validFilters[$filterValue])) { return $this->validFilters[$filterValue]; } foreach ($this->getExtensions() as $c) { if ($c->isFilterValid($filterValue) === true) { $this->validFilters[$filterValue] = true; return true; } } $this->validFilters[$filterValue] = false; return false; } /** * @param array $types * @param string $filter * @return array * @deprecated 11.0.0 - Use getFilterById()->filterTypes() instead */ public function filterNotificationTypes($types, $filter) { if (!$this->isFilterValid($filter)) { return $types; } foreach ($this->getExtensions() as $c) { $result = $c->filterNotificationTypes($types, $filter); if (is_array($result)) { $types = $result; } } return $types; } /** * @param string $filter * @return array * @deprecated 11.0.0 - Use getFilterById() instead */ public function getQueryForFilter($filter) { if (!$this->isFilterValid($filter)) { return [null, null]; } $conditions = array(); $parameters = array(); foreach ($this->getExtensions() as $c) { $result = $c->getQueryForFilter($filter); if (is_array($result)) { list($condition, $parameter) = $result; if ($condition && is_array($parameter)) { $conditions[] = $condition; $parameters = array_merge($parameters, $parameter); } } } if (empty($conditions)) { return array(null, null); } return array(' and ((' . implode(') or (', $conditions) . '))', $parameters); } /** * Will return additional notification types as specified by other apps * * @param string $languageCode * @return array * @deprecated 11.0.0 - Use getSettings() instead */ public function getNotificationTypes($languageCode) { $notificationTypes = $sharingNotificationTypes = []; foreach ($this->getExtensions() as $c) { $result = $c->getNotificationTypes($languageCode); if (is_array($result)) { $notificationTypes = array_merge($notificationTypes, $result); } } return array_merge($sharingNotificationTypes, $notificationTypes); } /** * @param string $method * @return array * @deprecated 11.0.0 - Use getSettings()->isDefaulEnabled<method>() instead */ public function getDefaultTypes($method) { $defaultTypes = array(); foreach ($this->getExtensions() as $c) { $types = $c->getDefaultTypes($method); if (is_array($types)) { $defaultTypes = array_merge($types, $defaultTypes); } } return $defaultTypes; } } private/Activity/EventMerger.php 0000604 00000016712 15247130451 0012753 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Activity; use OCP\Activity\IEvent; use OCP\Activity\IEventMerger; use OCP\IL10N; class EventMerger implements IEventMerger { /** @var IL10N */ protected $l10n; /** * @param IL10N $l10n */ public function __construct(IL10N $l10n) { $this->l10n = $l10n; } /** * Combines two events when possible to have grouping: * * Example1: Two events with subject '{user} created {file}' and * $mergeParameter file with different file and same user will be merged * to '{user} created {file1} and {file2}' and the childEvent on the return * will be set, if the events have been merged. * * Example2: Two events with subject '{user} created {file}' and * $mergeParameter file with same file and same user will be merged to * '{user} created {file1}' and the childEvent on the return will be set, if * the events have been merged. * * The following requirements have to be met, in order to be merged: * - Both events need to have the same `getApp()` * - Both events must not have a message `getMessage()` * - Both events need to have the same subject `getSubject()` * - Both events need to have the same object type `getObjectType()` * - The time difference between both events must not be bigger then 3 hours * - Only up to 5 events can be merged. * - All parameters apart from such starting with $mergeParameter must be * the same for both events. * * @param string $mergeParameter * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent */ public function mergeEvents($mergeParameter, IEvent $event, IEvent $previousEvent = null) { // No second event => can not combine if (!$previousEvent instanceof IEvent) { return $event; } // Different app => can not combine if ($event->getApp() !== $previousEvent->getApp()) { return $event; } // Message is set => can not combine if ($event->getMessage() !== '' || $previousEvent->getMessage() !== '') { return $event; } // Different subject => can not combine if ($event->getSubject() !== $previousEvent->getSubject()) { return $event; } // Different object type => can not combine if ($event->getObjectType() !== $previousEvent->getObjectType()) { return $event; } // More than 3 hours difference => can not combine if (abs($event->getTimestamp() - $previousEvent->getTimestamp()) > 3 * 60 * 60) { return $event; } // Other parameters are not the same => can not combine try { list($combined, $parameters) = $this->combineParameters($mergeParameter, $event, $previousEvent); } catch (\UnexpectedValueException $e) { return $event; } try { $newSubject = $this->getExtendedSubject($event->getRichSubject(), $mergeParameter, $combined); $parsedSubject = $this->generateParsedSubject($newSubject, $parameters); $event->setRichSubject($newSubject, $parameters) ->setParsedSubject($parsedSubject) ->setChildEvent($previousEvent); } catch (\UnexpectedValueException $e) { return $event; } return $event; } /** * @param string $mergeParameter * @param IEvent $event * @param IEvent $previousEvent * @return array * @throws \UnexpectedValueException */ protected function combineParameters($mergeParameter, IEvent $event, IEvent $previousEvent) { $params1 = $event->getRichSubjectParameters(); $params2 = $previousEvent->getRichSubjectParameters(); $params = []; $combined = 0; // Check that all parameters from $event exist in $previousEvent foreach ($params1 as $key => $parameter) { if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) { if (!$this->checkParameterAlreadyExits($params, $mergeParameter, $parameter)) { $combined++; $params[$mergeParameter . $combined] = $parameter; } continue; } if (!isset($params2[$key]) || $params2[$key] !== $parameter) { // Parameter missing on $previousEvent or different => can not combine throw new \UnexpectedValueException(); } $params[$key] = $parameter; } // Check that all parameters from $previousEvent exist in $event foreach ($params2 as $key => $parameter) { if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) { if (!$this->checkParameterAlreadyExits($params, $mergeParameter, $parameter)) { $combined++; $params[$mergeParameter . $combined] = $parameter; } continue; } if (!isset($params1[$key]) || $params1[$key] !== $parameter) { // Parameter missing on $event or different => can not combine throw new \UnexpectedValueException(); } $params[$key] = $parameter; } return [$combined, $params]; } /** * @param array[] $parameters * @param string $mergeParameter * @param array $parameter * @return bool */ protected function checkParameterAlreadyExits($parameters, $mergeParameter, $parameter) { foreach ($parameters as $key => $param) { if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) { if ($param === $parameter) { return true; } } } return false; } /** * @param string $subject * @param string $parameter * @param int $counter * @return mixed */ protected function getExtendedSubject($subject, $parameter, $counter) { switch ($counter) { case 1: $replacement = '{' . $parameter . '1}'; break; case 2: $replacement = $this->l10n->t( '%1$s and %2$s', ['{' . $parameter . '2}', '{' . $parameter . '1}'] ); break; case 3: $replacement = $this->l10n->t( '%1$s, %2$s and %3$s', ['{' . $parameter . '3}', '{' . $parameter . '2}', '{' . $parameter . '1}'] ); break; case 4: $replacement = $this->l10n->t( '%1$s, %2$s, %3$s and %4$s', ['{' . $parameter . '4}', '{' . $parameter . '3}', '{' . $parameter . '2}', '{' . $parameter . '1}'] ); break; case 5: $replacement = $this->l10n->t( '%1$s, %2$s, %3$s, %4$s and %5$s', ['{' . $parameter . '5}', '{' . $parameter . '4}', '{' . $parameter . '3}', '{' . $parameter . '2}', '{' . $parameter . '1}'] ); break; default: throw new \UnexpectedValueException(); } return str_replace( '{' . $parameter . '}', $replacement, $subject ); } /** * @param string $subject * @param array[] $parameters * @return string */ protected function generateParsedSubject($subject, $parameters) { $placeholders = $replacements = []; foreach ($parameters as $placeholder => $parameter) { $placeholders[] = '{' . $placeholder . '}'; if ($parameter['type'] === 'file') { $replacements[] = trim($parameter['path'], '/'); } else if (isset($parameter['name'])) { $replacements[] = $parameter['name']; } else { $replacements[] = $parameter['id']; } } return str_replace($placeholders, $replacements, $subject); } } private/Activity/LegacyFilter.php 0000604 00000005270 15247130451 0013077 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Activity; use OCP\Activity\IFilter; use OCP\Activity\IManager; class LegacyFilter implements IFilter { /** @var IManager */ protected $manager; /** @var string */ protected $identifier; /** @var string */ protected $name; /** @var bool */ protected $isTopFilter; /** * LegacySetting constructor. * * @param IManager $manager * @param string $identifier * @param string $name * @param bool $isTopFilter */ public function __construct(IManager $manager, $identifier, $name, $isTopFilter) { $this->manager = $manager; $this->identifier = $identifier; $this->name = $name; $this->isTopFilter = $isTopFilter; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return $this->identifier; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->name; } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return $this->isTopFilter ? 40 : 50; } /** * @return string Full URL to an icon, empty string when none is given * @since 11.0.0 */ public function getIcon() { // Old API was CSS class, so we can not use this... return ''; } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function filterTypes(array $types) { return $this->manager->filterNotificationTypes($types, $this->getIdentifier()); } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function allowedApps() { return []; } } private/Comments/Manager.php 0000604 00000064312 15247130451 0012072 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Comments; use Doctrine\DBAL\Exception\DriverException; use OCP\Comments\CommentsEvent; use OCP\Comments\IComment; use OCP\Comments\ICommentsEventHandler; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IConfig; use OCP\ILogger; use OCP\IUser; class Manager implements ICommentsManager { /** @var IDBConnection */ protected $dbConn; /** @var ILogger */ protected $logger; /** @var IConfig */ protected $config; /** @var IComment[] */ protected $commentsCache = []; /** @var \Closure[] */ protected $eventHandlerClosures = []; /** @var ICommentsEventHandler[] */ protected $eventHandlers = []; /** @var \Closure[] */ protected $displayNameResolvers = []; /** * Manager constructor. * * @param IDBConnection $dbConn * @param ILogger $logger * @param IConfig $config */ public function __construct( IDBConnection $dbConn, ILogger $logger, IConfig $config ) { $this->dbConn = $dbConn; $this->logger = $logger; $this->config = $config; } /** * converts data base data into PHP native, proper types as defined by * IComment interface. * * @param array $data * @return array */ protected function normalizeDatabaseData(array $data) { $data['id'] = strval($data['id']); $data['parent_id'] = strval($data['parent_id']); $data['topmost_parent_id'] = strval($data['topmost_parent_id']); $data['creation_timestamp'] = new \DateTime($data['creation_timestamp']); if (!is_null($data['latest_child_timestamp'])) { $data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']); } $data['children_count'] = intval($data['children_count']); return $data; } /** * prepares a comment for an insert or update operation after making sure * all necessary fields have a value assigned. * * @param IComment $comment * @return IComment returns the same updated IComment instance as provided * by parameter for convenience * @throws \UnexpectedValueException */ protected function prepareCommentForDatabaseWrite(IComment $comment) { if (!$comment->getActorType() || !$comment->getActorId() || !$comment->getObjectType() || !$comment->getObjectId() || !$comment->getVerb() ) { throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving'); } if ($comment->getId() === '') { $comment->setChildrenCount(0); $comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC'))); $comment->setLatestChildDateTime(null); } if (is_null($comment->getCreationDateTime())) { $comment->setCreationDateTime(new \DateTime()); } if ($comment->getParentId() !== '0') { $comment->setTopmostParentId($this->determineTopmostParentId($comment->getParentId())); } else { $comment->setTopmostParentId('0'); } $this->cache($comment); return $comment; } /** * returns the topmost parent id of a given comment identified by ID * * @param string $id * @return string * @throws NotFoundException */ protected function determineTopmostParentId($id) { $comment = $this->get($id); if ($comment->getParentId() === '0') { return $comment->getId(); } else { return $this->determineTopmostParentId($comment->getId()); } } /** * updates child information of a comment * * @param string $id * @param \DateTime $cDateTime the date time of the most recent child * @throws NotFoundException */ protected function updateChildrenInformation($id, \DateTime $cDateTime) { $qb = $this->dbConn->getQueryBuilder(); $query = $qb->select($qb->createFunction('COUNT(`id`)')) ->from('comments') ->where($qb->expr()->eq('parent_id', $qb->createParameter('id'))) ->setParameter('id', $id); $resultStatement = $query->execute(); $data = $resultStatement->fetch(\PDO::FETCH_NUM); $resultStatement->closeCursor(); $children = intval($data[0]); $comment = $this->get($id); $comment->setChildrenCount($children); $comment->setLatestChildDateTime($cDateTime); $this->save($comment); } /** * Tests whether actor or object type and id parameters are acceptable. * Throws exception if not. * * @param string $role * @param string $type * @param string $id * @throws \InvalidArgumentException */ protected function checkRoleParameters($role, $type, $id) { if ( !is_string($type) || empty($type) || !is_string($id) || empty($id) ) { throw new \InvalidArgumentException($role . ' parameters must be string and not empty'); } } /** * run-time caches a comment * * @param IComment $comment */ protected function cache(IComment $comment) { $id = $comment->getId(); if (empty($id)) { return; } $this->commentsCache[strval($id)] = $comment; } /** * removes an entry from the comments run time cache * * @param mixed $id the comment's id */ protected function uncache($id) { $id = strval($id); if (isset($this->commentsCache[$id])) { unset($this->commentsCache[$id]); } } /** * returns a comment instance * * @param string $id the ID of the comment * @return IComment * @throws NotFoundException * @throws \InvalidArgumentException * @since 9.0.0 */ public function get($id) { if (intval($id) === 0) { throw new \InvalidArgumentException('IDs must be translatable to a number in this implementation.'); } if (isset($this->commentsCache[$id])) { return $this->commentsCache[$id]; } $qb = $this->dbConn->getQueryBuilder(); $resultStatement = $qb->select('*') ->from('comments') ->where($qb->expr()->eq('id', $qb->createParameter('id'))) ->setParameter('id', $id, IQueryBuilder::PARAM_INT) ->execute(); $data = $resultStatement->fetch(); $resultStatement->closeCursor(); if (!$data) { throw new NotFoundException(); } $comment = new Comment($this->normalizeDatabaseData($data)); $this->cache($comment); return $comment; } /** * returns the comment specified by the id and all it's child comments. * At this point of time, we do only support one level depth. * * @param string $id * @param int $limit max number of entries to return, 0 returns all * @param int $offset the start entry * @return array * @since 9.0.0 * * The return array looks like this * [ * 'comment' => IComment, // root comment * 'replies' => * [ * 0 => * [ * 'comment' => IComment, * 'replies' => [] * ] * 1 => * [ * 'comment' => IComment, * 'replies'=> [] * ], * … * ] * ] */ public function getTree($id, $limit = 0, $offset = 0) { $tree = []; $tree['comment'] = $this->get($id); $tree['replies'] = []; $qb = $this->dbConn->getQueryBuilder(); $query = $qb->select('*') ->from('comments') ->where($qb->expr()->eq('topmost_parent_id', $qb->createParameter('id'))) ->orderBy('creation_timestamp', 'DESC') ->setParameter('id', $id); if ($limit > 0) { $query->setMaxResults($limit); } if ($offset > 0) { $query->setFirstResult($offset); } $resultStatement = $query->execute(); while ($data = $resultStatement->fetch()) { $comment = new Comment($this->normalizeDatabaseData($data)); $this->cache($comment); $tree['replies'][] = [ 'comment' => $comment, 'replies' => [] ]; } $resultStatement->closeCursor(); return $tree; } /** * returns comments for a specific object (e.g. a file). * * The sort order is always newest to oldest. * * @param string $objectType the object type, e.g. 'files' * @param string $objectId the id of the object * @param int $limit optional, number of maximum comments to be returned. if * not specified, all comments are returned. * @param int $offset optional, starting point * @param \DateTime $notOlderThan optional, timestamp of the oldest comments * that may be returned * @return IComment[] * @since 9.0.0 */ public function getForObject( $objectType, $objectId, $limit = 0, $offset = 0, \DateTime $notOlderThan = null ) { $comments = []; $qb = $this->dbConn->getQueryBuilder(); $query = $qb->select('*') ->from('comments') ->where($qb->expr()->eq('object_type', $qb->createParameter('type'))) ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id'))) ->orderBy('creation_timestamp', 'DESC') ->setParameter('type', $objectType) ->setParameter('id', $objectId); if ($limit > 0) { $query->setMaxResults($limit); } if ($offset > 0) { $query->setFirstResult($offset); } if (!is_null($notOlderThan)) { $query ->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan'))) ->setParameter('notOlderThan', $notOlderThan, 'datetime'); } $resultStatement = $query->execute(); while ($data = $resultStatement->fetch()) { $comment = new Comment($this->normalizeDatabaseData($data)); $this->cache($comment); $comments[] = $comment; } $resultStatement->closeCursor(); return $comments; } /** * @param $objectType string the object type, e.g. 'files' * @param $objectId string the id of the object * @param \DateTime $notOlderThan optional, timestamp of the oldest comments * that may be returned * @return Int * @since 9.0.0 */ public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null) { $qb = $this->dbConn->getQueryBuilder(); $query = $qb->select($qb->createFunction('COUNT(`id`)')) ->from('comments') ->where($qb->expr()->eq('object_type', $qb->createParameter('type'))) ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id'))) ->setParameter('type', $objectType) ->setParameter('id', $objectId); if (!is_null($notOlderThan)) { $query ->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan'))) ->setParameter('notOlderThan', $notOlderThan, 'datetime'); } $resultStatement = $query->execute(); $data = $resultStatement->fetch(\PDO::FETCH_NUM); $resultStatement->closeCursor(); return intval($data[0]); } /** * Get the number of unread comments for all files in a folder * * @param int $folderId * @param IUser $user * @return array [$fileId => $unreadCount] */ public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user) { $qb = $this->dbConn->getQueryBuilder(); $query = $qb->select('f.fileid') ->selectAlias( $qb->createFunction('COUNT(' . $qb->getColumnName('c.id') . ')'), 'num_ids' ) ->from('comments', 'c') ->innerJoin('c', 'filecache', 'f', $qb->expr()->andX( $qb->expr()->eq('c.object_type', $qb->createNamedParameter('files')), $qb->expr()->eq('f.fileid', $qb->expr()->castColumn('c.object_id', IQueryBuilder::PARAM_INT)) )) ->leftJoin('c', 'comments_read_markers', 'm', $qb->expr()->andX( $qb->expr()->eq('m.object_type', $qb->createNamedParameter('files')), $qb->expr()->eq('m.object_id', 'c.object_id'), $qb->expr()->eq('m.user_id', $qb->createNamedParameter($user->getUID())) )) ->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($folderId))) ->andWhere($qb->expr()->orX( $qb->expr()->gt('c.creation_timestamp', 'marker_datetime'), $qb->expr()->isNull('marker_datetime') )) ->groupBy('f.fileid'); $resultStatement = $query->execute(); $results = []; while ($row = $resultStatement->fetch()) { $results[$row['fileid']] = (int) $row['num_ids']; } $resultStatement->closeCursor(); return $results; } /** * creates a new comment and returns it. At this point of time, it is not * saved in the used data storage. Use save() after setting other fields * of the comment (e.g. message or verb). * * @param string $actorType the actor type (e.g. 'users') * @param string $actorId a user id * @param string $objectType the object type the comment is attached to * @param string $objectId the object id the comment is attached to * @return IComment * @since 9.0.0 */ public function create($actorType, $actorId, $objectType, $objectId) { $comment = new Comment(); $comment ->setActor($actorType, $actorId) ->setObject($objectType, $objectId); return $comment; } /** * permanently deletes the comment specified by the ID * * When the comment has child comments, their parent ID will be changed to * the parent ID of the item that is to be deleted. * * @param string $id * @return bool * @throws \InvalidArgumentException * @since 9.0.0 */ public function delete($id) { if (!is_string($id)) { throw new \InvalidArgumentException('Parameter must be string'); } try { $comment = $this->get($id); } catch (\Exception $e) { // Ignore exceptions, we just don't fire a hook then $comment = null; } $qb = $this->dbConn->getQueryBuilder(); $query = $qb->delete('comments') ->where($qb->expr()->eq('id', $qb->createParameter('id'))) ->setParameter('id', $id); try { $affectedRows = $query->execute(); $this->uncache($id); } catch (DriverException $e) { $this->logger->logException($e, ['app' => 'core_comments']); return false; } if ($affectedRows > 0 && $comment instanceof IComment) { $this->sendEvent(CommentsEvent::EVENT_DELETE, $comment); } return ($affectedRows > 0); } /** * saves the comment permanently * * if the supplied comment has an empty ID, a new entry comment will be * saved and the instance updated with the new ID. * * Otherwise, an existing comment will be updated. * * Throws NotFoundException when a comment that is to be updated does not * exist anymore at this point of time. * * @param IComment $comment * @return bool * @throws NotFoundException * @since 9.0.0 */ public function save(IComment $comment) { if ($this->prepareCommentForDatabaseWrite($comment)->getId() === '') { $result = $this->insert($comment); } else { $result = $this->update($comment); } if ($result && !!$comment->getParentId()) { $this->updateChildrenInformation( $comment->getParentId(), $comment->getCreationDateTime() ); $this->cache($comment); } return $result; } /** * inserts the provided comment in the database * * @param IComment $comment * @return bool */ protected function insert(IComment &$comment) { $qb = $this->dbConn->getQueryBuilder(); $affectedRows = $qb ->insert('comments') ->values([ 'parent_id' => $qb->createNamedParameter($comment->getParentId()), 'topmost_parent_id' => $qb->createNamedParameter($comment->getTopmostParentId()), 'children_count' => $qb->createNamedParameter($comment->getChildrenCount()), 'actor_type' => $qb->createNamedParameter($comment->getActorType()), 'actor_id' => $qb->createNamedParameter($comment->getActorId()), 'message' => $qb->createNamedParameter($comment->getMessage()), 'verb' => $qb->createNamedParameter($comment->getVerb()), 'creation_timestamp' => $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'), 'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'), 'object_type' => $qb->createNamedParameter($comment->getObjectType()), 'object_id' => $qb->createNamedParameter($comment->getObjectId()), ]) ->execute(); if ($affectedRows > 0) { $comment->setId(strval($qb->getLastInsertId())); $this->sendEvent(CommentsEvent::EVENT_ADD, $comment); } return $affectedRows > 0; } /** * updates a Comment data row * * @param IComment $comment * @return bool * @throws NotFoundException */ protected function update(IComment $comment) { // for properly working preUpdate Events we need the old comments as is // in the DB and overcome caching. Also avoid that outdated information stays. $this->uncache($comment->getId()); $this->sendEvent(CommentsEvent::EVENT_PRE_UPDATE, $this->get($comment->getId())); $this->uncache($comment->getId()); $qb = $this->dbConn->getQueryBuilder(); $affectedRows = $qb ->update('comments') ->set('parent_id', $qb->createNamedParameter($comment->getParentId())) ->set('topmost_parent_id', $qb->createNamedParameter($comment->getTopmostParentId())) ->set('children_count', $qb->createNamedParameter($comment->getChildrenCount())) ->set('actor_type', $qb->createNamedParameter($comment->getActorType())) ->set('actor_id', $qb->createNamedParameter($comment->getActorId())) ->set('message', $qb->createNamedParameter($comment->getMessage())) ->set('verb', $qb->createNamedParameter($comment->getVerb())) ->set('creation_timestamp', $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime')) ->set('latest_child_timestamp', $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime')) ->set('object_type', $qb->createNamedParameter($comment->getObjectType())) ->set('object_id', $qb->createNamedParameter($comment->getObjectId())) ->where($qb->expr()->eq('id', $qb->createParameter('id'))) ->setParameter('id', $comment->getId()) ->execute(); if ($affectedRows === 0) { throw new NotFoundException('Comment to update does ceased to exist'); } $this->sendEvent(CommentsEvent::EVENT_UPDATE, $comment); return $affectedRows > 0; } /** * removes references to specific actor (e.g. on user delete) of a comment. * The comment itself must not get lost/deleted. * * @param string $actorType the actor type (e.g. 'users') * @param string $actorId a user id * @return boolean * @since 9.0.0 */ public function deleteReferencesOfActor($actorType, $actorId) { $this->checkRoleParameters('Actor', $actorType, $actorId); $qb = $this->dbConn->getQueryBuilder(); $affectedRows = $qb ->update('comments') ->set('actor_type', $qb->createNamedParameter(ICommentsManager::DELETED_USER)) ->set('actor_id', $qb->createNamedParameter(ICommentsManager::DELETED_USER)) ->where($qb->expr()->eq('actor_type', $qb->createParameter('type'))) ->andWhere($qb->expr()->eq('actor_id', $qb->createParameter('id'))) ->setParameter('type', $actorType) ->setParameter('id', $actorId) ->execute(); $this->commentsCache = []; return is_int($affectedRows); } /** * deletes all comments made of a specific object (e.g. on file delete) * * @param string $objectType the object type (e.g. 'files') * @param string $objectId e.g. the file id * @return boolean * @since 9.0.0 */ public function deleteCommentsAtObject($objectType, $objectId) { $this->checkRoleParameters('Object', $objectType, $objectId); $qb = $this->dbConn->getQueryBuilder(); $affectedRows = $qb ->delete('comments') ->where($qb->expr()->eq('object_type', $qb->createParameter('type'))) ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id'))) ->setParameter('type', $objectType) ->setParameter('id', $objectId) ->execute(); $this->commentsCache = []; return is_int($affectedRows); } /** * deletes the read markers for the specified user * * @param \OCP\IUser $user * @return bool * @since 9.0.0 */ public function deleteReadMarksFromUser(IUser $user) { $qb = $this->dbConn->getQueryBuilder(); $query = $qb->delete('comments_read_markers') ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id'))) ->setParameter('user_id', $user->getUID()); try { $affectedRows = $query->execute(); } catch (DriverException $e) { $this->logger->logException($e, ['app' => 'core_comments']); return false; } return ($affectedRows > 0); } /** * sets the read marker for a given file to the specified date for the * provided user * * @param string $objectType * @param string $objectId * @param \DateTime $dateTime * @param IUser $user * @since 9.0.0 */ public function setReadMark($objectType, $objectId, \DateTime $dateTime, IUser $user) { $this->checkRoleParameters('Object', $objectType, $objectId); $qb = $this->dbConn->getQueryBuilder(); $values = [ 'user_id' => $qb->createNamedParameter($user->getUID()), 'marker_datetime' => $qb->createNamedParameter($dateTime, 'datetime'), 'object_type' => $qb->createNamedParameter($objectType), 'object_id' => $qb->createNamedParameter($objectId), ]; // Strategy: try to update, if this does not return affected rows, do an insert. $affectedRows = $qb ->update('comments_read_markers') ->set('user_id', $values['user_id']) ->set('marker_datetime', $values['marker_datetime']) ->set('object_type', $values['object_type']) ->set('object_id', $values['object_id']) ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id'))) ->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type'))) ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id'))) ->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR) ->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR) ->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR) ->execute(); if ($affectedRows > 0) { return; } $qb->insert('comments_read_markers') ->values($values) ->execute(); } /** * returns the read marker for a given file to the specified date for the * provided user. It returns null, when the marker is not present, i.e. * no comments were marked as read. * * @param string $objectType * @param string $objectId * @param IUser $user * @return \DateTime|null * @since 9.0.0 */ public function getReadMark($objectType, $objectId, IUser $user) { $qb = $this->dbConn->getQueryBuilder(); $resultStatement = $qb->select('marker_datetime') ->from('comments_read_markers') ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id'))) ->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type'))) ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id'))) ->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR) ->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR) ->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR) ->execute(); $data = $resultStatement->fetch(); $resultStatement->closeCursor(); if (!$data || is_null($data['marker_datetime'])) { return null; } return new \DateTime($data['marker_datetime']); } /** * deletes the read markers on the specified object * * @param string $objectType * @param string $objectId * @return bool * @since 9.0.0 */ public function deleteReadMarksOnObject($objectType, $objectId) { $this->checkRoleParameters('Object', $objectType, $objectId); $qb = $this->dbConn->getQueryBuilder(); $query = $qb->delete('comments_read_markers') ->where($qb->expr()->eq('object_type', $qb->createParameter('object_type'))) ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id'))) ->setParameter('object_type', $objectType) ->setParameter('object_id', $objectId); try { $affectedRows = $query->execute(); } catch (DriverException $e) { $this->logger->logException($e, ['app' => 'core_comments']); return false; } return ($affectedRows > 0); } /** * registers an Entity to the manager, so event notifications can be send * to consumers of the comments infrastructure * * @param \Closure $closure */ public function registerEventHandler(\Closure $closure) { $this->eventHandlerClosures[] = $closure; $this->eventHandlers = []; } /** * registers a method that resolves an ID to a display name for a given type * * @param string $type * @param \Closure $closure * @throws \OutOfBoundsException * @since 11.0.0 * * Only one resolver shall be registered per type. Otherwise a * \OutOfBoundsException has to thrown. */ public function registerDisplayNameResolver($type, \Closure $closure) { if (!is_string($type)) { throw new \InvalidArgumentException('String expected.'); } if (isset($this->displayNameResolvers[$type])) { throw new \OutOfBoundsException('Displayname resolver for this type already registered'); } $this->displayNameResolvers[$type] = $closure; } /** * resolves a given ID of a given Type to a display name. * * @param string $type * @param string $id * @return string * @throws \OutOfBoundsException * @since 11.0.0 * * If a provided type was not registered, an \OutOfBoundsException shall * be thrown. It is upon the resolver discretion what to return of the * provided ID is unknown. It must be ensured that a string is returned. */ public function resolveDisplayName($type, $id) { if (!is_string($type)) { throw new \InvalidArgumentException('String expected.'); } if (!isset($this->displayNameResolvers[$type])) { throw new \OutOfBoundsException('No Displayname resolver for this type registered'); } return (string)$this->displayNameResolvers[$type]($id); } /** * returns valid, registered entities * * @return \OCP\Comments\ICommentsEventHandler[] */ private function getEventHandlers() { if (!empty($this->eventHandlers)) { return $this->eventHandlers; } $this->eventHandlers = []; foreach ($this->eventHandlerClosures as $name => $closure) { $entity = $closure(); if (!($entity instanceof ICommentsEventHandler)) { throw new \InvalidArgumentException('The given entity does not implement the ICommentsEntity interface'); } $this->eventHandlers[$name] = $entity; } return $this->eventHandlers; } /** * sends notifications to the registered entities * * @param $eventType * @param IComment $comment */ private function sendEvent($eventType, IComment $comment) { $entities = $this->getEventHandlers(); $event = new CommentsEvent($eventType, $comment); foreach ($entities as $entity) { $entity->handle($event); } } } private/Comments/ManagerFactory.php 0000604 00000003374 15247130451 0013423 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Comments; use OCP\Comments\ICommentsManager; use OCP\Comments\ICommentsManagerFactory; use OCP\IServerContainer; class ManagerFactory implements ICommentsManagerFactory { /** * Server container * * @var IServerContainer */ private $serverContainer; /** * Constructor for the comments manager factory * * @param IServerContainer $serverContainer server container */ public function __construct(IServerContainer $serverContainer) { $this->serverContainer = $serverContainer; } /** * creates and returns an instance of the ICommentsManager * * @return ICommentsManager * @since 9.0.0 */ public function getManager() { return new Manager( $this->serverContainer->getDatabaseConnection(), $this->serverContainer->getLogger(), $this->serverContainer->getConfig(), $this->serverContainer->getEventDispatcher() ); } } private/Comments/Comment.php 0000604 00000023110 15247130451 0012111 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Comments; use OCP\Comments\IComment; use OCP\Comments\IllegalIDChangeException; use OCP\Comments\MessageTooLongException; class Comment implements IComment { protected $data = [ 'id' => '', 'parentId' => '0', 'topmostParentId' => '0', 'childrenCount' => '0', 'message' => '', 'verb' => '', 'actorType' => '', 'actorId' => '', 'objectType' => '', 'objectId' => '', 'creationDT' => null, 'latestChildDT' => null, ]; /** * Comment constructor. * * @param array $data optional, array with keys according to column names from * the comments database scheme */ public function __construct(array $data = null) { if(is_array($data)) { $this->fromArray($data); } } /** * returns the ID of the comment * * It may return an empty string, if the comment was not stored. * It is expected that the concrete Comment implementation gives an ID * by itself (e.g. after saving). * * @return string * @since 9.0.0 */ public function getId() { return $this->data['id']; } /** * sets the ID of the comment and returns itself * * It is only allowed to set the ID only, if the current id is an empty * string (which means it is not stored in a database, storage or whatever * the concrete implementation does), or vice versa. Changing a given ID is * not permitted and must result in an IllegalIDChangeException. * * @param string $id * @return IComment * @throws IllegalIDChangeException * @since 9.0.0 */ public function setId($id) { if(!is_string($id)) { throw new \InvalidArgumentException('String expected.'); } $id = trim($id); if($this->data['id'] === '' || ($this->data['id'] !== '' && $id === '')) { $this->data['id'] = $id; return $this; } throw new IllegalIDChangeException('Not allowed to assign a new ID to an already saved comment.'); } /** * returns the parent ID of the comment * * @return string * @since 9.0.0 */ public function getParentId() { return $this->data['parentId']; } /** * sets the parent ID and returns itself * * @param string $parentId * @return IComment * @since 9.0.0 */ public function setParentId($parentId) { if(!is_string($parentId)) { throw new \InvalidArgumentException('String expected.'); } $this->data['parentId'] = trim($parentId); return $this; } /** * returns the topmost parent ID of the comment * * @return string * @since 9.0.0 */ public function getTopmostParentId() { return $this->data['topmostParentId']; } /** * sets the topmost parent ID and returns itself * * @param string $id * @return IComment * @since 9.0.0 */ public function setTopmostParentId($id) { if(!is_string($id)) { throw new \InvalidArgumentException('String expected.'); } $this->data['topmostParentId'] = trim($id); return $this; } /** * returns the number of children * * @return int * @since 9.0.0 */ public function getChildrenCount() { return $this->data['childrenCount']; } /** * sets the number of children * * @param int $count * @return IComment * @since 9.0.0 */ public function setChildrenCount($count) { if(!is_int($count)) { throw new \InvalidArgumentException('Integer expected.'); } $this->data['childrenCount'] = $count; return $this; } /** * returns the message of the comment * * @return string * @since 9.0.0 */ public function getMessage() { return $this->data['message']; } /** * sets the message of the comment and returns itself * * @param string $message * @return IComment * @throws MessageTooLongException * @since 9.0.0 */ public function setMessage($message) { if(!is_string($message)) { throw new \InvalidArgumentException('String expected.'); } $message = trim($message); if(mb_strlen($message, 'UTF-8') > IComment::MAX_MESSAGE_LENGTH) { throw new MessageTooLongException('Comment message must not exceed ' . IComment::MAX_MESSAGE_LENGTH . ' characters'); } $this->data['message'] = $message; return $this; } /** * returns an array containing mentions that are included in the comment * * @return array each mention provides a 'type' and an 'id', see example below * @since 11.0.0 * * The return array looks like: * [ * [ * 'type' => 'user', * 'id' => 'citizen4' * ], * [ * 'type' => 'group', * 'id' => 'media' * ], * … * ] * */ public function getMentions() { $ok = preg_match_all('/\B@[a-z0-9_\-@\.\']+/i', $this->getMessage(), $mentions); if(!$ok || !isset($mentions[0]) || !is_array($mentions[0])) { return []; } $uids = array_unique($mentions[0]); $result = []; foreach ($uids as $uid) { // exclude author, no self-mentioning if($uid === '@' . $this->getActorId()) { continue; } $result[] = ['type' => 'user', 'id' => substr($uid, 1)]; } return $result; } /** * returns the verb of the comment * * @return string * @since 9.0.0 */ public function getVerb() { return $this->data['verb']; } /** * sets the verb of the comment, e.g. 'comment' or 'like' * * @param string $verb * @return IComment * @since 9.0.0 */ public function setVerb($verb) { if(!is_string($verb) || !trim($verb)) { throw new \InvalidArgumentException('Non-empty String expected.'); } $this->data['verb'] = trim($verb); return $this; } /** * returns the actor type * * @return string * @since 9.0.0 */ public function getActorType() { return $this->data['actorType']; } /** * returns the actor ID * * @return string * @since 9.0.0 */ public function getActorId() { return $this->data['actorId']; } /** * sets (overwrites) the actor type and id * * @param string $actorType e.g. 'users' * @param string $actorId e.g. 'zombie234' * @return IComment * @since 9.0.0 */ public function setActor($actorType, $actorId) { if( !is_string($actorType) || !trim($actorType) || !is_string($actorId) || !trim($actorId) ) { throw new \InvalidArgumentException('String expected.'); } $this->data['actorType'] = trim($actorType); $this->data['actorId'] = trim($actorId); return $this; } /** * returns the creation date of the comment. * * If not explicitly set, it shall default to the time of initialization. * * @return \DateTime * @since 9.0.0 */ public function getCreationDateTime() { return $this->data['creationDT']; } /** * sets the creation date of the comment and returns itself * * @param \DateTime $timestamp * @return IComment * @since 9.0.0 */ public function setCreationDateTime(\DateTime $timestamp) { $this->data['creationDT'] = $timestamp; return $this; } /** * returns the DateTime of the most recent child, if set, otherwise null * * @return \DateTime|null * @since 9.0.0 */ public function getLatestChildDateTime() { return $this->data['latestChildDT']; } /** * sets the date of the most recent child * * @param \DateTime $dateTime * @return IComment * @since 9.0.0 */ public function setLatestChildDateTime(\DateTime $dateTime = null) { $this->data['latestChildDT'] = $dateTime; return $this; } /** * returns the object type the comment is attached to * * @return string * @since 9.0.0 */ public function getObjectType() { return $this->data['objectType']; } /** * returns the object id the comment is attached to * * @return string * @since 9.0.0 */ public function getObjectId() { return $this->data['objectId']; } /** * sets (overwrites) the object of the comment * * @param string $objectType e.g. 'files' * @param string $objectId e.g. '16435' * @return IComment * @since 9.0.0 */ public function setObject($objectType, $objectId) { if( !is_string($objectType) || !trim($objectType) || !is_string($objectId) || !trim($objectId) ) { throw new \InvalidArgumentException('String expected.'); } $this->data['objectType'] = trim($objectType); $this->data['objectId'] = trim($objectId); return $this; } /** * sets the comment data based on an array with keys as taken from the * database. * * @param array $data * @return IComment */ protected function fromArray($data) { foreach(array_keys($data) as $key) { // translate DB keys to internal setter names $setter = 'set' . implode('', array_map('ucfirst', explode('_', $key))); $setter = str_replace('Timestamp', 'DateTime', $setter); if(method_exists($this, $setter)) { $this->$setter($data[$key]); } } foreach(['actor', 'object'] as $role) { if(isset($data[$role . '_type']) && isset($data[$role . '_id'])) { $setter = 'set' . ucfirst($role); $this->$setter($data[$role . '_type'], $data[$role . '_id']); } } return $this; } } private/Federation/CloudIdManager.php 0000604 00000005765 15247130451 0013640 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Federation; use OCP\Federation\ICloudId; use OCP\Federation\ICloudIdManager; class CloudIdManager implements ICloudIdManager { /** * @param string $cloudId * @return ICloudId * @throws \InvalidArgumentException */ public function resolveCloudId($cloudId) { // TODO magic here to get the url and user instead of just splitting on @ if (!$this->isValidCloudId($cloudId)) { throw new \InvalidArgumentException('Invalid cloud id'); } // Find the first character that is not allowed in user names $id = $this->fixRemoteURL($cloudId); $posSlash = strpos($id, '/'); $posColon = strpos($id, ':'); if ($posSlash === false && $posColon === false) { $invalidPos = strlen($id); } else if ($posSlash === false) { $invalidPos = $posColon; } else if ($posColon === false) { $invalidPos = $posSlash; } else { $invalidPos = min($posSlash, $posColon); } // Find the last @ before $invalidPos $pos = $lastAtPos = 0; while ($lastAtPos !== false && $lastAtPos <= $invalidPos) { $pos = $lastAtPos; $lastAtPos = strpos($id, '@', $pos + 1); } if ($pos !== false) { $user = substr($id, 0, $pos); $remote = substr($id, $pos + 1); if (!empty($user) && !empty($remote)) { return new CloudId($id, $user, $remote); } } throw new \InvalidArgumentException('Invalid cloud id'); } /** * @param string $user * @param string $remote * @return CloudId */ public function getCloudId($user, $remote) { // TODO check what the correct url is for remote (asking the remote) return new CloudId($user. '@' . $remote, $user, $remote); } /** * Strips away a potential file names and trailing slashes: * - http://localhost * - http://localhost/ * - http://localhost/index.php * - http://localhost/index.php/s/{shareToken} * * all return: http://localhost * * @param string $remote * @return string */ protected function fixRemoteURL($remote) { $remote = str_replace('\\', '/', $remote); if ($fileNamePosition = strpos($remote, '/index.php')) { $remote = substr($remote, 0, $fileNamePosition); } $remote = rtrim($remote, '/'); return $remote; } /** * @param string $cloudId * @return bool */ public function isValidCloudId($cloudId) { return strpos($cloudId, '@') !== false; } } private/Federation/CloudId.php 0000604 00000003226 15247130451 0012333 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Federation; use OCP\Federation\ICloudId; class CloudId implements ICloudId { /** @var string */ private $id; /** @var string */ private $user; /** @var string */ private $remote; /** * CloudId constructor. * * @param string $id * @param string $user * @param string $remote */ public function __construct($id, $user, $remote) { $this->id = $id; $this->user = $user; $this->remote = $remote; } /** * The full remote cloud id * * @return string */ public function getId() { return $this->id; } public function getDisplayId() { return str_replace('https://', '', str_replace('http://', '', $this->getId())); } /** * The username on the remote server * * @return string */ public function getUser() { return $this->user; } /** * The base address of the remote server * * @return string */ public function getRemote() { return $this->remote; } } private/User/Manager.php 0000604 00000035516 15247130451 0011227 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael U <mdusher@users.noreply.github.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Chan <plus.vincchan@gmail.com> * @author Volkan Gezer <volkangezer@gmail.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\User; use OC\Hooks\PublicEmitter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IUser; use OCP\IUserBackend; use OCP\IUserManager; use OCP\IConfig; use OCP\UserInterface; /** * Class Manager * * Hooks available in scope \OC\User: * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword) * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword) * - preDelete(\OC\User\User $user) * - postDelete(\OC\User\User $user) * - preCreateUser(string $uid, string $password) * - postCreateUser(\OC\User\User $user, string $password) * - change(\OC\User\User $user) * * @package OC\User */ class Manager extends PublicEmitter implements IUserManager { /** * @var \OCP\UserInterface[] $backends */ private $backends = array(); /** * @var \OC\User\User[] $cachedUsers */ private $cachedUsers = array(); /** * @var \OCP\IConfig $config */ private $config; /** * @param \OCP\IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; $cachedUsers = &$this->cachedUsers; $this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) { /** @var \OC\User\User $user */ unset($cachedUsers[$user->getUID()]); }); } /** * Get the active backends * @return \OCP\UserInterface[] */ public function getBackends() { return $this->backends; } /** * register a user backend * * @param \OCP\UserInterface $backend */ public function registerBackend($backend) { $this->backends[] = $backend; } /** * remove a user backend * * @param \OCP\UserInterface $backend */ public function removeBackend($backend) { $this->cachedUsers = array(); if (($i = array_search($backend, $this->backends)) !== false) { unset($this->backends[$i]); } } /** * remove all user backends */ public function clearBackends() { $this->cachedUsers = array(); $this->backends = array(); } /** * get a user by user id * * @param string $uid * @return \OC\User\User|null Either the user or null if the specified user does not exist */ public function get($uid) { if (is_null($uid) || $uid === '' || $uid === false) { return null; } if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends return $this->cachedUsers[$uid]; } foreach ($this->backends as $backend) { if ($backend->userExists($uid)) { return $this->getUserObject($uid, $backend); } } return null; } /** * get or construct the user object * * @param string $uid * @param \OCP\UserInterface $backend * @param bool $cacheUser If false the newly created user object will not be cached * @return \OC\User\User */ protected function getUserObject($uid, $backend, $cacheUser = true) { if (isset($this->cachedUsers[$uid])) { return $this->cachedUsers[$uid]; } if (method_exists($backend, 'loginName2UserName')) { $loginName = $backend->loginName2UserName($uid); if ($loginName !== false) { $uid = $loginName; } if (isset($this->cachedUsers[$uid])) { return $this->cachedUsers[$uid]; } } $user = new User($uid, $backend, $this, $this->config); if ($cacheUser) { $this->cachedUsers[$uid] = $user; } return $user; } /** * check if a user exists * * @param string $uid * @return bool */ public function userExists($uid) { $user = $this->get($uid); return ($user !== null); } /** * Check if the password is valid for the user * * @param string $loginName * @param string $password * @return mixed the User object on success, false otherwise */ public function checkPassword($loginName, $password) { $result = $this->checkPasswordNoLogging($loginName, $password); if ($result === false) { \OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']); } return $result; } /** * Check if the password is valid for the user * * @internal * @param string $loginName * @param string $password * @return mixed the User object on success, false otherwise */ public function checkPasswordNoLogging($loginName, $password) { $loginName = str_replace("\0", '', $loginName); $password = str_replace("\0", '', $password); foreach ($this->backends as $backend) { if ($backend->implementsActions(Backend::CHECK_PASSWORD)) { $uid = $backend->checkPassword($loginName, $password); if ($uid !== false) { return $this->getUserObject($uid, $backend); } } } return false; } /** * search by user id * * @param string $pattern * @param int $limit * @param int $offset * @return \OC\User\User[] */ public function search($pattern, $limit = null, $offset = null) { $users = array(); foreach ($this->backends as $backend) { $backendUsers = $backend->getUsers($pattern, $limit, $offset); if (is_array($backendUsers)) { foreach ($backendUsers as $uid) { $users[$uid] = $this->getUserObject($uid, $backend); } } } uasort($users, function ($a, $b) { /** * @var \OC\User\User $a * @var \OC\User\User $b */ return strcmp($a->getUID(), $b->getUID()); }); return $users; } /** * search by displayName * * @param string $pattern * @param int $limit * @param int $offset * @return \OC\User\User[] */ public function searchDisplayName($pattern, $limit = null, $offset = null) { $users = array(); foreach ($this->backends as $backend) { $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset); if (is_array($backendUsers)) { foreach ($backendUsers as $uid => $displayName) { $users[] = $this->getUserObject($uid, $backend); } } } usort($users, function ($a, $b) { /** * @var \OC\User\User $a * @var \OC\User\User $b */ return strcmp(strtolower($a->getDisplayName()), strtolower($b->getDisplayName())); }); return $users; } /** * @param string $uid * @param string $password * @throws \InvalidArgumentException * @return bool|IUser the created user or false */ public function createUser($uid, $password) { $localBackends = []; foreach ($this->backends as $backend) { if ($backend instanceof Database) { // First check if there is another user backend $localBackends[] = $backend; continue; } if ($backend->implementsActions(Backend::CREATE_USER)) { return $this->createUserFromBackend($uid, $password, $backend); } } foreach ($localBackends as $backend) { if ($backend->implementsActions(Backend::CREATE_USER)) { return $this->createUserFromBackend($uid, $password, $backend); } } return false; } /** * @param string $uid * @param string $password * @param UserInterface $backend * @return IUser|null * @throws \InvalidArgumentException */ public function createUserFromBackend($uid, $password, UserInterface $backend) { $l = \OC::$server->getL10N('lib'); // Check the name for bad characters // Allowed are: "a-z", "A-Z", "0-9" and "_.@-'" if (preg_match('/[^a-zA-Z0-9 _\.@\-\']/', $uid)) { throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:' . ' "a-z", "A-Z", "0-9", and "_.@-\'"')); } // No empty username if (trim($uid) === '') { throw new \InvalidArgumentException($l->t('A valid username must be provided')); } // No whitespace at the beginning or at the end if (trim($uid) !== $uid) { throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end')); } // Username only consists of 1 or 2 dots (directory traversal) if ($uid === '.' || $uid === '..') { throw new \InvalidArgumentException($l->t('Username must not consist of dots only')); } // No empty password if (trim($password) === '') { throw new \InvalidArgumentException($l->t('A valid password must be provided')); } // Check if user already exists if ($this->userExists($uid)) { throw new \InvalidArgumentException($l->t('The username is already being used')); } $this->emit('\OC\User', 'preCreateUser', [$uid, $password]); $backend->createUser($uid, $password); $user = $this->getUserObject($uid, $backend); if ($user instanceof IUser) { $this->emit('\OC\User', 'postCreateUser', [$user, $password]); } return $user; } /** * returns how many users per backend exist (if supported by backend) * * @param boolean $hasLoggedIn when true only users that have a lastLogin * entry in the preferences table will be affected * @return array|int an array of backend class as key and count number as value * if $hasLoggedIn is true only an int is returned */ public function countUsers($hasLoggedIn = false) { if ($hasLoggedIn) { return $this->countSeenUsers(); } $userCountStatistics = []; foreach ($this->backends as $backend) { if ($backend->implementsActions(Backend::COUNT_USERS)) { $backendUsers = $backend->countUsers(); if($backendUsers !== false) { if($backend instanceof IUserBackend) { $name = $backend->getBackendName(); } else { $name = get_class($backend); } if(isset($userCountStatistics[$name])) { $userCountStatistics[$name] += $backendUsers; } else { $userCountStatistics[$name] = $backendUsers; } } } } return $userCountStatistics; } /** * The callback is executed for each user on each backend. * If the callback returns false no further users will be retrieved. * * @param \Closure $callback * @param string $search * @param boolean $onlySeen when true only users that have a lastLogin entry * in the preferences table will be affected * @since 9.0.0 */ public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) { if ($onlySeen) { $this->callForSeenUsers($callback); } else { foreach ($this->getBackends() as $backend) { $limit = 500; $offset = 0; do { $users = $backend->getUsers($search, $limit, $offset); foreach ($users as $uid) { if (!$backend->userExists($uid)) { continue; } $user = $this->getUserObject($uid, $backend, false); $return = $callback($user); if ($return === false) { break; } } $offset += $limit; } while (count($users) >= $limit); } } } /** * returns how many users have logged in once * * @return int * @since 12.0.0 */ public function countDisabledUsers() { $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); $queryBuilder->select($queryBuilder->createFunction('COUNT(*)')) ->from('preferences') ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core'))) ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled'))) ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR)); $query = $queryBuilder->execute(); $result = (int)$query->fetchColumn(); $query->closeCursor(); return $result; } /** * returns how many users have logged in once * * @return int * @since 11.0.0 */ public function countSeenUsers() { $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); $queryBuilder->select($queryBuilder->createFunction('COUNT(*)')) ->from('preferences') ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login'))) ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin'))) ->andWhere($queryBuilder->expr()->isNotNull('configvalue')); $query = $queryBuilder->execute(); $result = (int)$query->fetchColumn(); $query->closeCursor(); return $result; } /** * @param \Closure $callback * @since 11.0.0 */ public function callForSeenUsers(\Closure $callback) { $limit = 1000; $offset = 0; do { $userIds = $this->getSeenUserIds($limit, $offset); $offset += $limit; foreach ($userIds as $userId) { foreach ($this->backends as $backend) { if ($backend->userExists($userId)) { $user = $this->getUserObject($userId, $backend, false); $return = $callback($user); if ($return === false) { return; } } } } } while (count($userIds) >= $limit); } /** * Getting all userIds that have a listLogin value requires checking the * value in php because on oracle you cannot use a clob in a where clause, * preventing us from doing a not null or length(value) > 0 check. * * @param int $limit * @param int $offset * @return string[] with user ids */ private function getSeenUserIds($limit = null, $offset = null) { $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); $queryBuilder->select(['userid']) ->from('preferences') ->where($queryBuilder->expr()->eq( 'appid', $queryBuilder->createNamedParameter('login')) ) ->andWhere($queryBuilder->expr()->eq( 'configkey', $queryBuilder->createNamedParameter('lastLogin')) ) ->andWhere($queryBuilder->expr()->isNotNull('configvalue') ); if ($limit !== null) { $queryBuilder->setMaxResults($limit); } if ($offset !== null) { $queryBuilder->setFirstResult($offset); } $query = $queryBuilder->execute(); $result = []; while ($row = $query->fetch()) { $result[] = $row['userid']; } $query->closeCursor(); return $result; } /** * @param string $email * @return IUser[] * @since 9.1.0 */ public function getByEmail($email) { $userIds = $this->config->getUsersForUserValue('settings', 'email', $email); return array_map(function($uid) { return $this->get($uid); }, $userIds); } } private/User/Database.php 0000604 00000024674 15247130451 0011364 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author adrien <adrien.waksberg@believedigital.com> * @author Aldo "xoen" Giambelluca <xoen@xoen.org> * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Björn Schießle <bjoern@schiessle.org> * @author fabian <fabian@web2.0-apps.de> * @author Georg Ehrke <georg@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author nishiki <nishiki@yaegashi.fr> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /* * * The following SQL statement is just a help for developers and will not be * executed! * * CREATE TABLE `users` ( * `uid` varchar(64) COLLATE utf8_unicode_ci NOT NULL, * `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL, * PRIMARY KEY (`uid`) * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; * */ namespace OC\User; use OC\Cache\CappedMemoryCache; use OCP\IUserBackend; use OCP\Util; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\GenericEvent; /** * Class for user management in a SQL Database (e.g. MySQL, SQLite) */ class Database extends Backend implements IUserBackend { /** @var CappedMemoryCache */ private $cache; /** @var EventDispatcher */ private $eventDispatcher; /** * \OC\User\Database constructor. * * @param EventDispatcher $eventDispatcher */ public function __construct($eventDispatcher = null) { $this->cache = new CappedMemoryCache(); $this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OC::$server->getEventDispatcher(); } /** * Create a new user * @param string $uid The username of the user to create * @param string $password The password of the new user * @return bool * * Creates a new user. Basic checking of username is done in OC_User * itself, not in its subclasses. */ public function createUser($uid, $password) { if (!$this->userExists($uid)) { $event = new GenericEvent($password); $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event); $query = \OC_DB::prepare('INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )'); $result = $query->execute(array($uid, \OC::$server->getHasher()->hash($password))); // Clear cache unset($this->cache[$uid]); return $result ? true : false; } return false; } /** * delete a user * @param string $uid The username of the user to delete * @return bool * * Deletes a user */ public function deleteUser($uid) { // Delete user-group-relation $query = \OC_DB::prepare('DELETE FROM `*PREFIX*users` WHERE `uid` = ?'); $result = $query->execute(array($uid)); if (isset($this->cache[$uid])) { unset($this->cache[$uid]); } return $result ? true : false; } /** * Set password * @param string $uid The username * @param string $password The new password * @return bool * * Change the password of a user */ public function setPassword($uid, $password) { if ($this->userExists($uid)) { $event = new GenericEvent($password); $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event); $query = \OC_DB::prepare('UPDATE `*PREFIX*users` SET `password` = ? WHERE `uid` = ?'); $result = $query->execute(array(\OC::$server->getHasher()->hash($password), $uid)); return $result ? true : false; } return false; } /** * Set display name * @param string $uid The username * @param string $displayName The new display name * @return bool * * Change the display name of a user */ public function setDisplayName($uid, $displayName) { if ($this->userExists($uid)) { $query = \OC_DB::prepare('UPDATE `*PREFIX*users` SET `displayname` = ? WHERE LOWER(`uid`) = LOWER(?)'); $query->execute(array($displayName, $uid)); $this->cache[$uid]['displayname'] = $displayName; return true; } return false; } /** * get display name of the user * @param string $uid user ID of the user * @return string display name */ public function getDisplayName($uid) { $this->loadUser($uid); return empty($this->cache[$uid]['displayname']) ? $uid : $this->cache[$uid]['displayname']; } /** * Get a list of all display names and user ids. * * @param string $search * @param string|null $limit * @param string|null $offset * @return array an array of all displayNames (value) and the corresponding uids (key) */ public function getDisplayNames($search = '', $limit = null, $offset = null) { $parameters = []; $searchLike = ''; if ($search !== '') { $parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%'; $parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%'; $searchLike = ' WHERE LOWER(`displayname`) LIKE LOWER(?) OR ' . 'LOWER(`uid`) LIKE LOWER(?)'; } $displayNames = array(); $query = \OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users`' . $searchLike .' ORDER BY LOWER(`displayname`), LOWER(`uid`) ASC', $limit, $offset); $result = $query->execute($parameters); while ($row = $result->fetchRow()) { $displayNames[$row['uid']] = $row['displayname']; } return $displayNames; } /** * Check if the password is correct * @param string $uid The username * @param string $password The password * @return string * * Check if the password is correct without logging in the user * returns the user id or false */ public function checkPassword($uid, $password) { $query = \OC_DB::prepare('SELECT `uid`, `password` FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)'); $result = $query->execute(array($uid)); $row = $result->fetchRow(); if ($row) { $storedHash = $row['password']; $newHash = ''; if(\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) { if(!empty($newHash)) { $this->setPassword($uid, $password); } return $row['uid']; } } return false; } /** * Load an user in the cache * @param string $uid the username * @return boolean true if user was found, false otherwise */ private function loadUser($uid) { $uid = (string) $uid; if (!isset($this->cache[$uid])) { //guests $uid could be NULL or '' if ($uid === '') { $this->cache[$uid]=false; return true; } $query = \OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)'); $result = $query->execute(array($uid)); if ($result === false) { Util::writeLog('core', \OC_DB::getErrorMessage(), Util::ERROR); return false; } $this->cache[$uid] = false; // "uid" is primary key, so there can only be a single result if ($row = $result->fetchRow()) { $this->cache[$uid]['uid'] = $row['uid']; $this->cache[$uid]['displayname'] = $row['displayname']; $result->closeCursor(); } else { $result->closeCursor(); return false; } } return true; } /** * Get a list of all users * * @param string $search * @param null|int $limit * @param null|int $offset * @return string[] an array of all uids */ public function getUsers($search = '', $limit = null, $offset = null) { $parameters = []; $searchLike = ''; if ($search !== '') { $parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%'; $searchLike = ' WHERE LOWER(`uid`) LIKE LOWER(?)'; } $query = \OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users`' . $searchLike . ' ORDER BY LOWER(`uid`) ASC', $limit, $offset); $result = $query->execute($parameters); $users = array(); while ($row = $result->fetchRow()) { $users[] = $row['uid']; } return $users; } /** * check if a user exists * @param string $uid the username * @return boolean */ public function userExists($uid) { $this->loadUser($uid); return $this->cache[$uid] !== false; } /** * get the user's home directory * @param string $uid the username * @return string|false */ public function getHome($uid) { if ($this->userExists($uid)) { return \OC::$server->getConfig()->getSystemValue("datadirectory", \OC::$SERVERROOT . "/data") . '/' . $uid; } return false; } /** * @return bool */ public function hasUserListings() { return true; } /** * counts the users in the database * * @return int|bool */ public function countUsers() { $query = \OC_DB::prepare('SELECT COUNT(*) FROM `*PREFIX*users`'); $result = $query->execute(); if ($result === false) { Util::writeLog('core', \OC_DB::getErrorMessage(), Util::ERROR); return false; } return $result->fetchOne(); } /** * returns the username for the given login name in the correct casing * * @param string $loginName * @return string|false */ public function loginName2UserName($loginName) { if ($this->userExists($loginName)) { return $this->cache[$loginName]['uid']; } return false; } /** * Backend name to be shown in user management * @return string the name of the backend to be shown */ public function getBackendName(){ return 'Database'; } public static function preLoginNameUsedAsUserName($param) { if(!isset($param['uid'])) { throw new \Exception('key uid is expected to be set in $param'); } $backends = \OC::$server->getUserManager()->getBackends(); foreach ($backends as $backend) { if ($backend instanceof Database) { /** @var \OC\User\Database $backend */ $uid = $backend->loginName2UserName($param['uid']); if ($uid !== false) { $param['uid'] = $uid; return; } } } } } private/User/Session.php 0000604 00000062050 15247130451 0011271 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Sandro Lutz <sandro.lutz@temparus.ch> * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Sandro Lutz <sandro.lutz@temparus.ch> * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Christoph Wurst <christoph@owncloud.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * @author Felix Rupp <kontakt@felixrupp.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\User; use OC; use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; use OC\Hooks\Emitter; use OC\Hooks\PublicEmitter; use OC_User; use OC_Util; use OCA\DAV\Connector\Sabre\Auth; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\NotPermittedException; use OCP\IConfig; use OCP\IRequest; use OCP\ISession; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; use OCP\Lockdown\ILockdownManager; use OCP\Security\ISecureRandom; use OCP\Session\Exceptions\SessionNotAvailableException; use OCP\Util; use Symfony\Component\EventDispatcher\GenericEvent; /** * Class Session * * Hooks available in scope \OC\User: * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword) * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword) * - preDelete(\OC\User\User $user) * - postDelete(\OC\User\User $user) * - preCreateUser(string $uid, string $password) * - postCreateUser(\OC\User\User $user) * - preLogin(string $user, string $password) * - postLogin(\OC\User\User $user, string $password) * - preRememberedLogin(string $uid) * - postRememberedLogin(\OC\User\User $user) * - logout() * - postLogout() * * @package OC\User */ class Session implements IUserSession, Emitter { /** @var IUserManager|PublicEmitter $manager */ private $manager; /** @var ISession $session */ private $session; /** @var ITimeFactory */ private $timeFactory; /** @var IProvider */ private $tokenProvider; /** @var IConfig */ private $config; /** @var User $activeUser */ protected $activeUser; /** @var ISecureRandom */ private $random; /** @var ILockdownManager */ private $lockdownManager; /** * @param IUserManager $manager * @param ISession $session * @param ITimeFactory $timeFactory * @param IProvider $tokenProvider * @param IConfig $config * @param ISecureRandom $random * @param ILockdownManager $lockdownManager */ public function __construct(IUserManager $manager, ISession $session, ITimeFactory $timeFactory, $tokenProvider, IConfig $config, ISecureRandom $random, ILockdownManager $lockdownManager ) { $this->manager = $manager; $this->session = $session; $this->timeFactory = $timeFactory; $this->tokenProvider = $tokenProvider; $this->config = $config; $this->random = $random; $this->lockdownManager = $lockdownManager; } /** * @param IProvider $provider */ public function setTokenProvider(IProvider $provider) { $this->tokenProvider = $provider; } /** * @param string $scope * @param string $method * @param callable $callback */ public function listen($scope, $method, callable $callback) { $this->manager->listen($scope, $method, $callback); } /** * @param string $scope optional * @param string $method optional * @param callable $callback optional */ public function removeListener($scope = null, $method = null, callable $callback = null) { $this->manager->removeListener($scope, $method, $callback); } /** * get the manager object * * @return Manager|PublicEmitter */ public function getManager() { return $this->manager; } /** * get the session object * * @return ISession */ public function getSession() { return $this->session; } /** * set the session object * * @param ISession $session */ public function setSession(ISession $session) { if ($this->session instanceof ISession) { $this->session->close(); } $this->session = $session; $this->activeUser = null; } /** * set the currently active user * * @param IUser|null $user */ public function setUser($user) { if (is_null($user)) { $this->session->remove('user_id'); } else { $this->session->set('user_id', $user->getUID()); } $this->activeUser = $user; } /** * get the current active user * * @return IUser|null Current user, otherwise null */ public function getUser() { // FIXME: This is a quick'n dirty work-around for the incognito mode as // described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155 if (OC_User::isIncognitoMode()) { return null; } if (is_null($this->activeUser)) { $uid = $this->session->get('user_id'); if (is_null($uid)) { return null; } $this->activeUser = $this->manager->get($uid); if (is_null($this->activeUser)) { return null; } $this->validateSession(); } return $this->activeUser; } /** * Validate whether the current session is valid * * - For token-authenticated clients, the token validity is checked * - For browsers, the session token validity is checked */ protected function validateSession() { $token = null; $appPassword = $this->session->get('app_password'); if (is_null($appPassword)) { try { $token = $this->session->getId(); } catch (SessionNotAvailableException $ex) { return; } } else { $token = $appPassword; } if (!$this->validateToken($token)) { // Session was invalidated $this->logout(); } } /** * Checks whether the user is logged in * * @return bool if logged in */ public function isLoggedIn() { $user = $this->getUser(); if (is_null($user)) { return false; } return $user->isEnabled(); } /** * set the login name * * @param string|null $loginName for the logged in user */ public function setLoginName($loginName) { if (is_null($loginName)) { $this->session->remove('loginname'); } else { $this->session->set('loginname', $loginName); } } /** * get the login name of the current user * * @return string */ public function getLoginName() { if ($this->activeUser) { return $this->session->get('loginname'); } else { $uid = $this->session->get('user_id'); if ($uid) { $this->activeUser = $this->manager->get($uid); return $this->session->get('loginname'); } else { return null; } } } /** * set the token id * * @param int|null $token that was used to log in */ protected function setToken($token) { if ($token === null) { $this->session->remove('token-id'); } else { $this->session->set('token-id', $token); } } /** * try to log in with the provided credentials * * @param string $uid * @param string $password * @return boolean|null * @throws LoginException */ public function login($uid, $password) { $this->session->regenerateId(); if ($this->validateToken($password, $uid)) { return $this->loginWithToken($password); } return $this->loginWithPassword($uid, $password); } /** * @param IUser $user * @param array $loginDetails * @param bool $regenerateSessionId * @return true returns true if login successful or an exception otherwise * @throws LoginException */ public function completeLogin(IUser $user, array $loginDetails, $regenerateSessionId = true) { if (!$user->isEnabled()) { // disabled users can not log in // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory $message = \OC::$server->getL10N('lib')->t('User disabled'); throw new LoginException($message); } if($regenerateSessionId) { $this->session->regenerateId(); } $this->setUser($user); $this->setLoginName($loginDetails['loginName']); if(isset($loginDetails['token']) && $loginDetails['token'] instanceof IToken) { $this->setToken($loginDetails['token']->getId()); $this->lockdownManager->setToken($loginDetails['token']); $firstTimeLogin = false; } else { $this->setToken(null); $firstTimeLogin = $user->updateLastLoginTimestamp(); } $this->manager->emit('\OC\User', 'postLogin', [$user, $loginDetails['password']]); if($this->isLoggedIn()) { $this->prepareUserLogin($firstTimeLogin, $regenerateSessionId); return true; } else { $message = \OC::$server->getL10N('lib')->t('Login canceled by app'); throw new LoginException($message); } } /** * Tries to log in a client * * Checks token auth enforced * Checks 2FA enabled * * @param string $user * @param string $password * @param IRequest $request * @param OC\Security\Bruteforce\Throttler $throttler * @throws LoginException * @throws PasswordLoginForbiddenException * @return boolean */ public function logClientIn($user, $password, IRequest $request, OC\Security\Bruteforce\Throttler $throttler) { $currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login'); if ($this->manager instanceof PublicEmitter) { $this->manager->emit('\OC\User', 'preLogin', array($user, $password)); } $isTokenPassword = $this->isTokenPassword($password); if (!$isTokenPassword && $this->isTokenAuthEnforced()) { throw new PasswordLoginForbiddenException(); } if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) { throw new PasswordLoginForbiddenException(); } if (!$this->login($user, $password) ) { $users = $this->manager->getByEmail($user); if (count($users) === 1) { return $this->login($users[0]->getUID(), $password); } $throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]); if($currentDelay === 0) { $throttler->sleepDelay($request->getRemoteAddress(), 'login'); } return false; } if ($isTokenPassword) { $this->session->set('app_password', $password); } else if($this->supportsCookies($request)) { // Password login, but cookies supported -> create (browser) session token $this->createSessionToken($request, $this->getUser()->getUID(), $user, $password); } return true; } protected function supportsCookies(IRequest $request) { if (!is_null($request->getCookie('cookie_test'))) { return true; } setcookie('cookie_test', 'test', $this->timeFactory->getTime() + 3600); return false; } private function isTokenAuthEnforced() { return $this->config->getSystemValue('token_auth_enforced', false); } protected function isTwoFactorEnforced($username) { Util::emitHook( '\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', array('uid' => &$username) ); $user = $this->manager->get($username); if (is_null($user)) { $users = $this->manager->getByEmail($username); if (empty($users)) { return false; } if (count($users) !== 1) { return true; } $user = $users[0]; } // DI not possible due to cyclic dependencies :'-/ return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user); } /** * Check if the given 'password' is actually a device token * * @param string $password * @return boolean */ public function isTokenPassword($password) { try { $this->tokenProvider->getToken($password); return true; } catch (InvalidTokenException $ex) { return false; } } protected function prepareUserLogin($firstTimeLogin, $refreshCsrfToken = true) { if ($refreshCsrfToken) { // TODO: mock/inject/use non-static // Refresh the token \OC::$server->getCsrfTokenManager()->refreshToken(); } //we need to pass the user name, which may differ from login name $user = $this->getUser()->getUID(); OC_Util::setupFS($user); if ($firstTimeLogin) { // TODO: lock necessary? //trigger creation of user home and /files folder $userFolder = \OC::$server->getUserFolder($user); try { // copy skeleton \OC_Util::copySkeleton($user, $userFolder); } catch (NotPermittedException $ex) { // read only uses } // trigger any other initialization \OC::$server->getEventDispatcher()->dispatch(IUser::class . '::firstLogin', new GenericEvent($this->getUser())); } } /** * Tries to login the user with HTTP Basic Authentication * * @todo do not allow basic auth if the user is 2FA enforced * @param IRequest $request * @param OC\Security\Bruteforce\Throttler $throttler * @return boolean if the login was successful */ public function tryBasicAuthLogin(IRequest $request, OC\Security\Bruteforce\Throttler $throttler) { if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) { try { if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request, $throttler)) { /** * Add DAV authenticated. This should in an ideal world not be * necessary but the iOS App reads cookies from anywhere instead * only the DAV endpoint. * This makes sure that the cookies will be valid for the whole scope * @see https://github.com/owncloud/core/issues/22893 */ $this->session->set( Auth::DAV_AUTHENTICATED, $this->getUser()->getUID() ); // Set the last-password-confirm session to make the sudo mode work $this->session->set('last-password-confirm', $this->timeFactory->getTime()); return true; } } catch (PasswordLoginForbiddenException $ex) { // Nothing to do } } return false; } /** * Log an user in via login name and password * * @param string $uid * @param string $password * @return boolean * @throws LoginException if an app canceld the login process or the user is not enabled */ private function loginWithPassword($uid, $password) { $user = $this->manager->checkPassword($uid, $password); if ($user === false) { // Password check failed return false; } return $this->completeLogin($user, ['loginName' => $uid, 'password' => $password], false); } /** * Log an user in with a given token (id) * * @param string $token * @return boolean * @throws LoginException if an app canceled the login process or the user is not enabled */ private function loginWithToken($token) { try { $dbToken = $this->tokenProvider->getToken($token); } catch (InvalidTokenException $ex) { return false; } $uid = $dbToken->getUID(); // When logging in with token, the password must be decrypted first before passing to login hook $password = ''; try { $password = $this->tokenProvider->getPassword($dbToken, $token); } catch (PasswordlessTokenException $ex) { // Ignore and use empty string instead } $this->manager->emit('\OC\User', 'preLogin', array($uid, $password)); $user = $this->manager->get($uid); if (is_null($user)) { // user does not exist return false; } return $this->completeLogin( $user, [ 'loginName' => $dbToken->getLoginName(), 'password' => $password, 'token' => $dbToken ], false); } /** * Create a new session token for the given user credentials * * @param IRequest $request * @param string $uid user UID * @param string $loginName login name * @param string $password * @param int $remember * @return boolean */ public function createSessionToken(IRequest $request, $uid, $loginName, $password = null, $remember = IToken::DO_NOT_REMEMBER) { if (is_null($this->manager->get($uid))) { // User does not exist return false; } $name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser'; try { $sessionId = $this->session->getId(); $pwd = $this->getPassword($password); $this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name, IToken::TEMPORARY_TOKEN, $remember); return true; } catch (SessionNotAvailableException $ex) { // This can happen with OCC, where a memory session is used // if a memory session is used, we shouldn't create a session token anyway return false; } } /** * Checks if the given password is a token. * If yes, the password is extracted from the token. * If no, the same password is returned. * * @param string $password either the login password or a device token * @return string|null the password or null if none was set in the token */ private function getPassword($password) { if (is_null($password)) { // This is surely no token ;-) return null; } try { $token = $this->tokenProvider->getToken($password); try { return $this->tokenProvider->getPassword($token, $password); } catch (PasswordlessTokenException $ex) { return null; } } catch (InvalidTokenException $ex) { return $password; } } /** * @param IToken $dbToken * @param string $token * @return boolean */ private function checkTokenCredentials(IToken $dbToken, $token) { // Check whether login credentials are still valid and the user was not disabled // This check is performed each 5 minutes $lastCheck = $dbToken->getLastCheck() ? : 0; $now = $this->timeFactory->getTime(); if ($lastCheck > ($now - 60 * 5)) { // Checked performed recently, nothing to do now return true; } try { $pwd = $this->tokenProvider->getPassword($dbToken, $token); } catch (InvalidTokenException $ex) { // An invalid token password was used -> log user out return false; } catch (PasswordlessTokenException $ex) { // Token has no password if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) { $this->tokenProvider->invalidateToken($token); return false; } $dbToken->setLastCheck($now); return true; } if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false || (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) { $this->tokenProvider->invalidateToken($token); // Password has changed or user was disabled -> log user out return false; } $dbToken->setLastCheck($now); return true; } /** * Check if the given token exists and performs password/user-enabled checks * * Invalidates the token if checks fail * * @param string $token * @param string $user login name * @return boolean */ private function validateToken($token, $user = null) { try { $dbToken = $this->tokenProvider->getToken($token); } catch (InvalidTokenException $ex) { return false; } // Check if login names match if (!is_null($user) && $dbToken->getLoginName() !== $user) { // TODO: this makes it imposssible to use different login names on browser and client // e.g. login by e-mail 'user@example.com' on browser for generating the token will not // allow to use the client token with the login name 'user'. return false; } if (!$this->checkTokenCredentials($dbToken, $token)) { return false; } $this->tokenProvider->updateTokenActivity($dbToken); return true; } /** * Tries to login the user with auth token header * * @param IRequest $request * @todo check remember me cookie * @return boolean */ public function tryTokenLogin(IRequest $request) { $authHeader = $request->getHeader('Authorization'); if (strpos($authHeader, 'Bearer ') === false) { // No auth header, let's try session id try { $token = $this->session->getId(); } catch (SessionNotAvailableException $ex) { return false; } } else { $token = substr($authHeader, 7); } if (!$this->loginWithToken($token)) { return false; } if(!$this->validateToken($token)) { return false; } return true; } /** * perform login using the magic cookie (remember login) * * @param string $uid the username * @param string $currentToken * @param string $oldSessionId * @return bool */ public function loginWithCookie($uid, $currentToken, $oldSessionId) { $this->session->regenerateId(); $this->manager->emit('\OC\User', 'preRememberedLogin', array($uid)); $user = $this->manager->get($uid); if (is_null($user)) { // user does not exist return false; } // get stored tokens $tokens = $this->config->getUserKeys($uid, 'login_token'); // test cookies token against stored tokens if (!in_array($currentToken, $tokens, true)) { return false; } // replace successfully used token with a new one $this->config->deleteUserValue($uid, 'login_token', $currentToken); $newToken = $this->random->generate(32); $this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFactory->getTime()); try { $sessionId = $this->session->getId(); $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId); } catch (SessionNotAvailableException $ex) { return false; } catch (InvalidTokenException $ex) { \OC::$server->getLogger()->warning('Renewing session token failed', ['app' => 'core']); return false; } $this->setMagicInCookie($user->getUID(), $newToken); $token = $this->tokenProvider->getToken($sessionId); //login $this->setUser($user); $this->setLoginName($token->getLoginName()); $this->setToken($token->getId()); $this->lockdownManager->setToken($token); $user->updateLastLoginTimestamp(); $password = null; try { $password = $this->tokenProvider->getPassword($token, $sessionId); } catch (PasswordlessTokenException $ex) { // Ignore } $this->manager->emit('\OC\User', 'postRememberedLogin', [$user, $password]); return true; } /** * @param IUser $user */ public function createRememberMeToken(IUser $user) { $token = $this->random->generate(32); $this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFactory->getTime()); $this->setMagicInCookie($user->getUID(), $token); } /** * logout the user from the session */ public function logout() { $this->manager->emit('\OC\User', 'logout'); $user = $this->getUser(); if (!is_null($user)) { try { $this->tokenProvider->invalidateToken($this->session->getId()); } catch (SessionNotAvailableException $ex) { } } $this->setUser(null); $this->setLoginName(null); $this->setToken(null); $this->unsetMagicInCookie(); $this->session->clear(); $this->manager->emit('\OC\User', 'postLogout'); } /** * Set cookie value to use in next page load * * @param string $username username to be set * @param string $token */ public function setMagicInCookie($username, $token) { $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https'; $webRoot = \OC::$WEBROOT; if ($webRoot === '') { $webRoot = '/'; } $expires = $this->timeFactory->getTime() + $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true); setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true); try { setcookie('nc_session_id', $this->session->getId(), $expires, $webRoot, '', $secureCookie, true); } catch (SessionNotAvailableException $ex) { // ignore } } /** * Remove cookie for "remember username" */ public function unsetMagicInCookie() { //TODO: DI for cookies and IRequest $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https'; unset($_COOKIE['nc_username']); //TODO: DI unset($_COOKIE['nc_token']); unset($_COOKIE['nc_session_id']); setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true); setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true); setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true); // old cookies might be stored under /webroot/ instead of /webroot // and Firefox doesn't like it! setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); } /** * Update password of the browser session token if there is one * * @param string $password */ public function updateSessionTokenPassword($password) { try { $sessionId = $this->session->getId(); $token = $this->tokenProvider->getToken($sessionId); $this->tokenProvider->setPassword($token, $sessionId, $password); } catch (SessionNotAvailableException $ex) { // Nothing to do } catch (InvalidTokenException $ex) { // Nothing to do } } } private/User/NoUserException.php 0000604 00000001466 15247130451 0012744 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\User; class NoUserException extends \Exception {} private/User/LoginException.php 0000604 00000001536 15247130451 0012577 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\User; class LoginException extends \Exception { } private/User/Backend.php 0000604 00000007767 15247130451 0011213 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\User; use \OCP\UserInterface; /** * Abstract base class for user management. Provides methods for querying backend * capabilities. */ abstract class Backend implements UserInterface { /** * error code for functions not provided by the user backend */ const NOT_IMPLEMENTED = -501; /** * actions that user backends can define */ const CREATE_USER = 1; // 1 << 0 const SET_PASSWORD = 16; // 1 << 4 const CHECK_PASSWORD = 256; // 1 << 8 const GET_HOME = 4096; // 1 << 12 const GET_DISPLAYNAME = 65536; // 1 << 16 const SET_DISPLAYNAME = 1048576; // 1 << 20 const PROVIDE_AVATAR = 16777216; // 1 << 24 const COUNT_USERS = 268435456; // 1 << 28 protected $possibleActions = array( self::CREATE_USER => 'createUser', self::SET_PASSWORD => 'setPassword', self::CHECK_PASSWORD => 'checkPassword', self::GET_HOME => 'getHome', self::GET_DISPLAYNAME => 'getDisplayName', self::SET_DISPLAYNAME => 'setDisplayName', self::PROVIDE_AVATAR => 'canChangeAvatar', self::COUNT_USERS => 'countUsers', ); /** * Get all supported actions * @return int bitwise-or'ed actions * * Returns the supported actions as int to be * compared with self::CREATE_USER etc. */ public function getSupportedActions() { $actions = 0; foreach($this->possibleActions AS $action => $methodName) { if(method_exists($this, $methodName)) { $actions |= $action; } } return $actions; } /** * Check if backend implements actions * @param int $actions bitwise-or'ed actions * @return boolean * * Returns the supported actions as int to be * compared with self::CREATE_USER etc. */ public function implementsActions($actions) { return (bool)($this->getSupportedActions() & $actions); } /** * delete a user * @param string $uid The username of the user to delete * @return bool * * Deletes a user */ public function deleteUser( $uid ) { return false; } /** * Get a list of all users * * @param string $search * @param null|int $limit * @param null|int $offset * @return string[] an array of all uids */ public function getUsers($search = '', $limit = null, $offset = null) { return array(); } /** * check if a user exists * @param string $uid the username * @return boolean */ public function userExists($uid) { return false; } /** * get the user's home directory * @param string $uid the username * @return boolean */ public function getHome($uid) { return false; } /** * get display name of the user * @param string $uid user ID of the user * @return string display name */ public function getDisplayName($uid) { return $uid; } /** * Get a list of all display names and user ids. * * @param string $search * @param string|null $limit * @param string|null $offset * @return array an array of all displayNames (value) and the corresponding uids (key) */ public function getDisplayNames($search = '', $limit = null, $offset = null) { $displayNames = array(); $users = $this->getUsers($search, $limit, $offset); foreach ( $users as $user) { $displayNames[$user] = $user; } return $displayNames; } /** * Check if a user list is available or not * @return boolean if users can be listed or not */ public function hasUserListings() { return false; } } private/User/User.php 0000604 00000027617 15247130451 0010576 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\User; use OC\Accounts\AccountManager; use OC\Files\Cache\Storage; use OC\Hooks\Emitter; use OC_Helper; use OCP\IAvatarManager; use OCP\IImage; use OCP\IURLGenerator; use OCP\IUser; use OCP\IConfig; use OCP\UserInterface; use \OCP\IUserBackend; class User implements IUser { /** @var string $uid */ private $uid; /** @var string $displayName */ private $displayName; /** @var UserInterface $backend */ private $backend; /** @var bool $enabled */ private $enabled; /** @var Emitter|Manager $emitter */ private $emitter; /** @var string $home */ private $home; /** @var int $lastLogin */ private $lastLogin; /** @var \OCP\IConfig $config */ private $config; /** @var IAvatarManager */ private $avatarManager; /** @var IURLGenerator */ private $urlGenerator; /** * @param string $uid * @param UserInterface $backend * @param \OC\Hooks\Emitter $emitter * @param IConfig|null $config * @param IURLGenerator $urlGenerator */ public function __construct($uid, $backend, $emitter = null, IConfig $config = null, $urlGenerator = null) { $this->uid = $uid; $this->backend = $backend; $this->emitter = $emitter; if(is_null($config)) { $config = \OC::$server->getConfig(); } $this->config = $config; $this->urlGenerator = $urlGenerator; $enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true'); $this->enabled = ($enabled === 'true'); $this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0); if (is_null($this->urlGenerator)) { $this->urlGenerator = \OC::$server->getURLGenerator(); } } /** * get the user id * * @return string */ public function getUID() { return $this->uid; } /** * get the display name for the user, if no specific display name is set it will fallback to the user id * * @return string */ public function getDisplayName() { if (!isset($this->displayName)) { $displayName = ''; if ($this->backend and $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) { // get display name and strip whitespace from the beginning and end of it $backendDisplayName = $this->backend->getDisplayName($this->uid); if (is_string($backendDisplayName)) { $displayName = trim($backendDisplayName); } } if (!empty($displayName)) { $this->displayName = $displayName; } else { $this->displayName = $this->uid; } } return $this->displayName; } /** * set the displayname for the user * * @param string $displayName * @return bool */ public function setDisplayName($displayName) { $displayName = trim($displayName); if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName)) { $result = $this->backend->setDisplayName($this->uid, $displayName); if ($result) { $this->displayName = $displayName; $this->triggerChange('displayName', $displayName); } return $result !== false; } else { return false; } } /** * set the email address of the user * * @param string|null $mailAddress * @return void * @since 9.0.0 */ public function setEMailAddress($mailAddress) { $oldMailAddress = $this->getEMailAddress(); if($mailAddress === '') { $this->config->deleteUserValue($this->uid, 'settings', 'email'); } else { $this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress); } if($oldMailAddress !== $mailAddress) { $this->triggerChange('eMailAddress', $mailAddress, $oldMailAddress); } } /** * returns the timestamp of the user's last login or 0 if the user did never * login * * @return int */ public function getLastLogin() { return $this->lastLogin; } /** * updates the timestamp of the most recent login of this user */ public function updateLastLoginTimestamp() { $firstTimeLogin = ($this->lastLogin === 0); $this->lastLogin = time(); $this->config->setUserValue( $this->uid, 'login', 'lastLogin', $this->lastLogin); return $firstTimeLogin; } /** * Delete the user * * @return bool */ public function delete() { if ($this->emitter) { $this->emitter->emit('\OC\User', 'preDelete', array($this)); } // get the home now because it won't return it after user deletion $homePath = $this->getHome(); $result = $this->backend->deleteUser($this->uid); if ($result) { // FIXME: Feels like an hack - suggestions? $groupManager = \OC::$server->getGroupManager(); // We have to delete the user from all groups foreach ($groupManager->getUserGroupIds($this) as $groupId) { $group = $groupManager->get($groupId); if ($group) { \OC_Hook::emit("OC_Group", "pre_removeFromGroup", ["run" => true, "uid" => $this->uid, "gid" => $groupId]); $group->removeUser($this); \OC_Hook::emit("OC_User", "post_removeFromGroup", ["uid" => $this->uid, "gid" => $groupId]); } } // Delete the user's keys in preferences \OC::$server->getConfig()->deleteAllUserValues($this->uid); // Delete user files in /data/ if ($homePath !== false) { // FIXME: this operates directly on FS, should use View instead... // also this is not testable/mockable... \OC_Helper::rmdirr($homePath); } // Delete the users entry in the storage table Storage::remove('home::' . $this->uid); \OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid); \OC::$server->getCommentsManager()->deleteReadMarksFromUser($this); $notification = \OC::$server->getNotificationManager()->createNotification(); $notification->setUser($this->uid); \OC::$server->getNotificationManager()->markProcessed($notification); /** @var AccountManager $accountManager */ $accountManager = \OC::$server->query(AccountManager::class); $accountManager->deleteUser($this); if ($this->emitter) { $this->emitter->emit('\OC\User', 'postDelete', array($this)); } } return !($result === false); } /** * Set the password of the user * * @param string $password * @param string $recoveryPassword for the encryption app to reset encryption keys * @return bool */ public function setPassword($password, $recoveryPassword = null) { if ($this->emitter) { $this->emitter->emit('\OC\User', 'preSetPassword', array($this, $password, $recoveryPassword)); } if ($this->backend->implementsActions(Backend::SET_PASSWORD)) { $result = $this->backend->setPassword($this->uid, $password); if ($this->emitter) { $this->emitter->emit('\OC\User', 'postSetPassword', array($this, $password, $recoveryPassword)); } return !($result === false); } else { return false; } } /** * get the users home folder to mount * * @return string */ public function getHome() { if (!$this->home) { if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) { $this->home = $home; } elseif ($this->config) { $this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid; } else { $this->home = \OC::$SERVERROOT . '/data/' . $this->uid; } } return $this->home; } /** * Get the name of the backend class the user is connected with * * @return string */ public function getBackendClassName() { if($this->backend instanceof IUserBackend) { return $this->backend->getBackendName(); } return get_class($this->backend); } /** * check if the backend allows the user to change his avatar on Personal page * * @return bool */ public function canChangeAvatar() { if ($this->backend->implementsActions(Backend::PROVIDE_AVATAR)) { return $this->backend->canChangeAvatar($this->uid); } return true; } /** * check if the backend supports changing passwords * * @return bool */ public function canChangePassword() { return $this->backend->implementsActions(Backend::SET_PASSWORD); } /** * check if the backend supports changing display names * * @return bool */ public function canChangeDisplayName() { if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) { return false; } return $this->backend->implementsActions(Backend::SET_DISPLAYNAME); } /** * check if the user is enabled * * @return bool */ public function isEnabled() { return $this->enabled; } /** * set the enabled status for the user * * @param bool $enabled */ public function setEnabled($enabled) { $oldStatus = $this->isEnabled(); $this->enabled = $enabled; $enabled = ($enabled) ? 'true' : 'false'; if ($oldStatus !== $this->enabled) { $this->triggerChange('enabled', $enabled); $this->config->setUserValue($this->uid, 'core', 'enabled', $enabled); } } /** * get the users email address * * @return string|null * @since 9.0.0 */ public function getEMailAddress() { return $this->config->getUserValue($this->uid, 'settings', 'email', null); } /** * get the users' quota * * @return string * @since 9.0.0 */ public function getQuota() { $quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default'); if($quota === 'default') { $quota = $this->config->getAppValue('files', 'default_quota', 'none'); } return $quota; } /** * set the users' quota * * @param string $quota * @return void * @since 9.0.0 */ public function setQuota($quota) { $oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', ''); if($quota !== 'none' and $quota !== 'default') { $quota = OC_Helper::computerFileSize($quota); $quota = OC_Helper::humanFileSize($quota); } $this->config->setUserValue($this->uid, 'files', 'quota', $quota); if($quota !== $oldQuota) { $this->triggerChange('quota', $quota); } } /** * get the avatar image if it exists * * @param int $size * @return IImage|null * @since 9.0.0 */ public function getAvatarImage($size) { // delay the initialization if (is_null($this->avatarManager)) { $this->avatarManager = \OC::$server->getAvatarManager(); } $avatar = $this->avatarManager->getAvatar($this->uid); $image = $avatar->get(-1); if ($image) { return $image; } return null; } /** * get the federation cloud id * * @return string * @since 9.0.0 */ public function getCloudId() { $uid = $this->getUID(); $server = $this->urlGenerator->getAbsoluteURL('/'); $server = rtrim( $this->removeProtocolFromUrl($server), '/'); return \OC::$server->getCloudIdManager()->getCloudId($uid, $server)->getId(); } /** * @param string $url * @return string */ private function removeProtocolFromUrl($url) { if (strpos($url, 'https://') === 0) { return substr($url, strlen('https://')); } else if (strpos($url, 'http://') === 0) { return substr($url, strlen('http://')); } return $url; } public function triggerChange($feature, $value = null, $oldValue = null) { if ($this->emitter) { $this->emitter->emit('\OC\User', 'changeUser', array($this, $feature, $value, $oldValue)); } } } private/ContactsManager.php 0000604 00000012720 15247130451 0012000 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tobia De Koninck <tobia@ledfan.be> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC { class ContactsManager implements \OCP\Contacts\IManager { /** * This function is used to search and find contacts within the users address books. * In case $pattern is empty all contacts will be returned. * * @param string $pattern which should match within the $searchProperties * @param array $searchProperties defines the properties within the query pattern should match * @param array $options - for future use. One should always have options! * @return array an array of contacts which are arrays of key-value-pairs */ public function search($pattern, $searchProperties = array(), $options = array()) { $this->loadAddressBooks(); $result = array(); foreach($this->addressBooks as $addressBook) { $r = $addressBook->search($pattern, $searchProperties, $options); $contacts = array(); foreach($r as $c){ $c['addressbook-key'] = $addressBook->getKey(); $contacts[] = $c; } $result = array_merge($result, $contacts); } return $result; } /** * This function can be used to delete the contact identified by the given id * * @param object $id the unique identifier to a contact * @param string $addressBookKey identifier of the address book in which the contact shall be deleted * @return bool successful or not */ public function delete($id, $addressBookKey) { $addressBook = $this->getAddressBook($addressBookKey); if (!$addressBook) { return null; } if ($addressBook->getPermissions() & \OCP\Constants::PERMISSION_DELETE) { return $addressBook->delete($id); } return null; } /** * This function is used to create a new contact if 'id' is not given or not present. * Otherwise the contact will be updated by replacing the entire data set. * * @param array $properties this array if key-value-pairs defines a contact * @param string $addressBookKey identifier of the address book in which the contact shall be created or updated * @return array representing the contact just created or updated */ public function createOrUpdate($properties, $addressBookKey) { $addressBook = $this->getAddressBook($addressBookKey); if (!$addressBook) { return null; } if ($addressBook->getPermissions() & \OCP\Constants::PERMISSION_CREATE) { return $addressBook->createOrUpdate($properties); } return null; } /** * Check if contacts are available (e.g. contacts app enabled) * * @return bool true if enabled, false if not */ public function isEnabled() { return !empty($this->addressBooks) || !empty($this->addressBookLoaders); } /** * @param \OCP\IAddressBook $addressBook */ public function registerAddressBook(\OCP\IAddressBook $addressBook) { $this->addressBooks[$addressBook->getKey()] = $addressBook; } /** * @param \OCP\IAddressBook $addressBook */ public function unregisterAddressBook(\OCP\IAddressBook $addressBook) { unset($this->addressBooks[$addressBook->getKey()]); } /** * @return array */ public function getAddressBooks() { $this->loadAddressBooks(); $result = array(); foreach($this->addressBooks as $addressBook) { $result[$addressBook->getKey()] = $addressBook->getDisplayName(); } return $result; } /** * removes all registered address book instances */ public function clear() { $this->addressBooks = array(); $this->addressBookLoaders = array(); } /** * @var \OCP\IAddressBook[] which holds all registered address books */ private $addressBooks = array(); /** * @var \Closure[] to call to load/register address books */ private $addressBookLoaders = array(); /** * In order to improve lazy loading a closure can be registered which will be called in case * address books are actually requested * * @param \Closure $callable */ public function register(\Closure $callable) { $this->addressBookLoaders[] = $callable; } /** * Get (and load when needed) the address book for $key * * @param string $addressBookKey * @return \OCP\IAddressBook */ protected function getAddressBook($addressBookKey) { $this->loadAddressBooks(); if (!array_key_exists($addressBookKey, $this->addressBooks)) { return null; } return $this->addressBooks[$addressBookKey]; } /** * Load all address books registered with 'register' */ protected function loadAddressBooks() { foreach($this->addressBookLoaders as $callable) { $callable($this); } $this->addressBookLoaders = array(); } } } private/App/InfoParser.php 0000604 00000015735 15247130451 0011530 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> * * @author Andreas Fischer <bantu@owncloud.com> * @author Christoph Wurst <christoph@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App; use OCP\ICache; class InfoParser { /** @var \OCP\ICache|null */ private $cache; /** * @param ICache|null $cache */ public function __construct(ICache $cache = null) { $this->cache = $cache; } /** * @param string $file the xml file to be loaded * @return null|array where null is an indicator for an error */ public function parse($file) { if (!file_exists($file)) { return null; } if(!is_null($this->cache)) { $fileCacheKey = $file . filemtime($file); if ($cachedValue = $this->cache->get($fileCacheKey)) { return json_decode($cachedValue, true); } } libxml_use_internal_errors(true); $loadEntities = libxml_disable_entity_loader(false); $xml = simplexml_load_file($file); libxml_disable_entity_loader($loadEntities); if ($xml === false) { libxml_clear_errors(); return null; } $array = $this->xmlToArray($xml); if (is_null($array)) { return null; } if (!array_key_exists('info', $array)) { $array['info'] = []; } if (!array_key_exists('remote', $array)) { $array['remote'] = []; } if (!array_key_exists('public', $array)) { $array['public'] = []; } if (!array_key_exists('types', $array)) { $array['types'] = []; } if (!array_key_exists('repair-steps', $array)) { $array['repair-steps'] = []; } if (!array_key_exists('install', $array['repair-steps'])) { $array['repair-steps']['install'] = []; } if (!array_key_exists('pre-migration', $array['repair-steps'])) { $array['repair-steps']['pre-migration'] = []; } if (!array_key_exists('post-migration', $array['repair-steps'])) { $array['repair-steps']['post-migration'] = []; } if (!array_key_exists('live-migration', $array['repair-steps'])) { $array['repair-steps']['live-migration'] = []; } if (!array_key_exists('uninstall', $array['repair-steps'])) { $array['repair-steps']['uninstall'] = []; } if (!array_key_exists('background-jobs', $array)) { $array['background-jobs'] = []; } if (!array_key_exists('two-factor-providers', $array)) { $array['two-factor-providers'] = []; } if (!array_key_exists('commands', $array)) { $array['commands'] = []; } if (!array_key_exists('activity', $array)) { $array['activity'] = []; } if (!array_key_exists('filters', $array['activity'])) { $array['activity']['filters'] = []; } if (!array_key_exists('settings', $array['activity'])) { $array['activity']['settings'] = []; } if (!array_key_exists('providers', $array['activity'])) { $array['activity']['providers'] = []; } if (array_key_exists('types', $array)) { if (is_array($array['types'])) { foreach ($array['types'] as $type => $v) { unset($array['types'][$type]); if (is_string($type)) { $array['types'][] = $type; } } } else { $array['types'] = []; } } if (isset($array['repair-steps']['install']['step']) && is_array($array['repair-steps']['install']['step'])) { $array['repair-steps']['install'] = $array['repair-steps']['install']['step']; } if (isset($array['repair-steps']['pre-migration']['step']) && is_array($array['repair-steps']['pre-migration']['step'])) { $array['repair-steps']['pre-migration'] = $array['repair-steps']['pre-migration']['step']; } if (isset($array['repair-steps']['post-migration']['step']) && is_array($array['repair-steps']['post-migration']['step'])) { $array['repair-steps']['post-migration'] = $array['repair-steps']['post-migration']['step']; } if (isset($array['repair-steps']['live-migration']['step']) && is_array($array['repair-steps']['live-migration']['step'])) { $array['repair-steps']['live-migration'] = $array['repair-steps']['live-migration']['step']; } if (isset($array['repair-steps']['uninstall']['step']) && is_array($array['repair-steps']['uninstall']['step'])) { $array['repair-steps']['uninstall'] = $array['repair-steps']['uninstall']['step']; } if (isset($array['background-jobs']['job']) && is_array($array['background-jobs']['job'])) { $array['background-jobs'] = $array['background-jobs']['job']; } if (isset($array['commands']['command']) && is_array($array['commands']['command'])) { $array['commands'] = $array['commands']['command']; } if (isset($array['activity']['filters']['filter']) && is_array($array['activity']['filters']['filter'])) { $array['activity']['filters'] = $array['activity']['filters']['filter']; } if (isset($array['activity']['settings']['setting']) && is_array($array['activity']['settings']['setting'])) { $array['activity']['settings'] = $array['activity']['settings']['setting']; } if (isset($array['activity']['providers']['provider']) && is_array($array['activity']['providers']['provider'])) { $array['activity']['providers'] = $array['activity']['providers']['provider']; } if(!is_null($this->cache)) { $this->cache->set($fileCacheKey, json_encode($array)); } return $array; } /** * @param \SimpleXMLElement $xml * @return array */ function xmlToArray($xml) { if (!$xml->children()) { return (string)$xml; } $array = []; foreach ($xml->children() as $element => $node) { $totalElement = count($xml->{$element}); if (!isset($array[$element])) { $array[$element] = $totalElement > 1 ? [] : ""; } /** @var \SimpleXMLElement $node */ // Has attributes if ($attributes = $node->attributes()) { $data = [ '@attributes' => [], ]; if (!count($node->children())){ $value = (string)$node; if (!empty($value)) { $data['@value'] = (string)$node; } } else { $data = array_merge($data, $this->xmlToArray($node)); } foreach ($attributes as $attr => $value) { $data['@attributes'][$attr] = (string)$value; } if ($totalElement > 1) { $array[$element][] = $data; } else { $array[$element] = $data; } // Just a value } else { if ($totalElement > 1) { $array[$element][] = $this->xmlToArray($node); } else { $array[$element] = $this->xmlToArray($node); } } } return $array; } } private/App/CodeChecker/PrivateCheck.php 0000604 00000003640 15247130451 0014157 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; class PrivateCheck extends AbstractCheck implements ICheck { /** * @return string */ protected function getLocalDescription() { return 'private'; } /** * @return array */ public function getLocalClasses() { return [ // classes replaced by the public api 'OC_API' => '6.0.0', 'OC_App' => '6.0.0', 'OC_AppConfig' => '6.0.0', 'OC_Avatar' => '6.0.0', 'OC_BackgroundJob' => '6.0.0', 'OC_Config' => '6.0.0', 'OC_DB' => '6.0.0', 'OC_Files' => '6.0.0', 'OC_Helper' => '6.0.0', 'OC_Hook' => '6.0.0', 'OC_Image' => '6.0.0', 'OC_JSON' => '6.0.0', 'OC_L10N' => '6.0.0', 'OC_Log' => '6.0.0', 'OC_Mail' => '6.0.0', 'OC_Preferences' => '6.0.0', 'OC_Search_Provider' => '6.0.0', 'OC_Search_Result' => '6.0.0', 'OC_Request' => '6.0.0', 'OC_Response' => '6.0.0', 'OC_Template' => '6.0.0', 'OC_User' => '6.0.0', 'OC_Util' => '6.0.0', ]; } /** * @return array */ public function getLocalConstants() { return []; } /** * @return array */ public function getLocalFunctions() { return []; } /** * @return array */ public function getLocalMethods() { return []; } } private/App/CodeChecker/DatabaseSchemaChecker.php 0000604 00000007547 15247130451 0015733 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\CodeChecker; class DatabaseSchemaChecker { /** * @param string $appId * @return array */ public function analyse($appId) { $appPath = \OC_App::getAppPath($appId); if ($appPath === false) { throw new \RuntimeException("No app with given id <$appId> known."); } if (!file_exists($appPath . '/appinfo/database.xml')) { return ['errors' => [], 'warnings' => []]; } libxml_use_internal_errors(true); $loadEntities = libxml_disable_entity_loader(false); $xml = simplexml_load_file($appPath . '/appinfo/database.xml'); libxml_disable_entity_loader($loadEntities); $errors = $warnings = []; foreach ($xml->table as $table) { // Table names if (strpos($table->name, '*dbprefix*') !== 0) { $errors[] = 'Database schema error: name of table ' . $table->name . ' does not start with *dbprefix*'; } $tableName = substr($table->name, strlen('*dbprefix*')); if (strpos($tableName, '*dbprefix*') !== false) { $warnings[] = 'Database schema warning: *dbprefix* should only appear once in name of table ' . $table->name; } if (strlen($tableName) > 27) { $errors[] = 'Database schema error: Name of table ' . $table->name . ' is too long (' . strlen($tableName) . '), max. 27 characters (21 characters for tables with autoincrement) + *dbprefix* allowed'; } $hasAutoIncrement = false; // Column names foreach ($table->declaration->field as $column) { if (strpos($column->name, '*dbprefix*') !== false) { $warnings[] = 'Database schema warning: *dbprefix* should not appear in name of column ' . $column->name . ' on table ' . $table->name; } if (strlen($column->name) > 30) { $errors[] = 'Database schema error: Name of column ' . $column->name . ' on table ' . $table->name . ' is too long (' . strlen($tableName) . '), max. 30 characters allowed'; } if ($column->autoincrement) { if ($hasAutoIncrement) { $errors[] = 'Database schema error: Table ' . $table->name . ' has multiple autoincrement columns'; } if (strlen($tableName) > 21) { $errors[] = 'Database schema error: Name of table ' . $table->name . ' is too long (' . strlen($tableName) . '), max. 27 characters (21 characters for tables with autoincrement) + *dbprefix* allowed'; } $hasAutoIncrement = true; } } // Index names foreach ($table->declaration->index as $index) { $hasPrefix = strpos($index->name, '*dbprefix*'); if ($hasPrefix !== false && $hasPrefix !== 0) { $warnings[] = 'Database schema warning: *dbprefix* should only appear at the beginning in name of index ' . $index->name . ' on table ' . $table->name; } $indexName = $hasPrefix === 0 ? substr($index->name, strlen('*dbprefix*')) : $index->name; if (strlen($indexName) > 27) { $errors[] = 'Database schema error: Name of index ' . $index->name . ' on table ' . $table->name . ' is too long (' . strlen($tableName) . '), max. 27 characters + *dbprefix* allowed'; } } } return ['errors' => $errors, 'warnings' => $warnings]; } } private/App/CodeChecker/AbstractCheck.php 0000604 00000007120 15247130451 0014305 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; abstract class AbstractCheck implements ICheck { /** @var ICheck */ protected $check; /** * @param ICheck $check */ public function __construct(ICheck $check) { $this->check = $check; } /** * @param int $errorCode * @param string $errorObject * @return string */ public function getDescription($errorCode, $errorObject) { switch ($errorCode) { case CodeChecker::STATIC_CALL_NOT_ALLOWED: $functions = $this->getLocalFunctions(); $functions = array_change_key_case($functions, CASE_LOWER); if (isset($functions[$errorObject])) { return $this->getLocalDescription(); } // no break; case CodeChecker::CLASS_EXTENDS_NOT_ALLOWED: case CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED: case CodeChecker::CLASS_NEW_NOT_ALLOWED: case CodeChecker::CLASS_USE_NOT_ALLOWED: $classes = $this->getLocalClasses(); $classes = array_change_key_case($classes, CASE_LOWER); if (isset($classes[$errorObject])) { return $this->getLocalDescription(); } break; case CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED: $constants = $this->getLocalConstants(); $constants = array_change_key_case($constants, CASE_LOWER); if (isset($constants[$errorObject])) { return $this->getLocalDescription(); } break; case CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED: $methods = $this->getLocalMethods(); $methods = array_change_key_case($methods, CASE_LOWER); if (isset($methods[$errorObject])) { return $this->getLocalDescription(); } break; } return $this->check->getDescription($errorCode, $errorObject); } /** * @return string */ abstract protected function getLocalDescription(); /** * @return array */ abstract protected function getLocalClasses(); /** * @return array */ abstract protected function getLocalConstants(); /** * @return array */ abstract protected function getLocalFunctions(); /** * @return array */ abstract protected function getLocalMethods(); /** * @return array E.g.: `'ClassName' => 'oc version',` */ public function getClasses() { return array_merge($this->getLocalClasses(), $this->check->getClasses()); } /** * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',` */ public function getConstants() { return array_merge($this->getLocalConstants(), $this->check->getConstants()); } /** * @return array E.g.: `'functionName' => 'oc version',` */ public function getFunctions() { return array_merge($this->getLocalFunctions(), $this->check->getFunctions()); } /** * @return array E.g.: `'ClassName::methodName' => 'oc version',` */ public function getMethods() { return array_merge($this->getLocalMethods(), $this->check->getMethods()); } /** * @return bool */ public function checkStrongComparisons() { return $this->check->checkStrongComparisons(); } } private/App/CodeChecker/InfoChecker.php 0000604 00000010673 15247130451 0013773 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; use OC\App\InfoParser; use OC\Hooks\BasicEmitter; class InfoChecker extends BasicEmitter { /** @var InfoParser */ private $infoParser; private $mandatoryFields = [ 'author', 'description', 'id', 'licence', 'name', ]; private $optionalFields = [ 'bugs', 'category', 'default_enable', 'dependencies', // TODO: Mandatory as of ownCloud 11 'documentation', 'namespace', 'ocsid', 'public', 'remote', 'repository', 'types', 'version', 'website', ]; private $deprecatedFields = [ 'info', 'require', 'requiremax', 'requiremin', 'shipped', 'standalone', ]; public function __construct(InfoParser $infoParser) { $this->infoParser = $infoParser; } /** * @param string $appId * @return array */ public function analyse($appId) { $appPath = \OC_App::getAppPath($appId); if ($appPath === false) { throw new \RuntimeException("No app with given id <$appId> known."); } $errors = []; $info = $this->infoParser->parse($appPath . '/appinfo/info.xml'); if (isset($info['dependencies']['owncloud']['@attributes']['min-version']) && (isset($info['requiremin']) || isset($info['require']))) { $this->emit('InfoChecker', 'duplicateRequirement', ['min']); $errors[] = [ 'type' => 'duplicateRequirement', 'field' => 'min', ]; } else if ( !isset($info['dependencies']['owncloud']['@attributes']['min-version']) && !isset($info['dependencies']['nextcloud']['@attributes']['min-version']) ) { $this->emit('InfoChecker', 'missingRequirement', ['min']); } if (isset($info['dependencies']['owncloud']['@attributes']['max-version']) && isset($info['requiremax'])) { $this->emit('InfoChecker', 'duplicateRequirement', ['max']); $errors[] = [ 'type' => 'duplicateRequirement', 'field' => 'max', ]; } else if ( !isset($info['dependencies']['owncloud']['@attributes']['max-version']) && !isset($info['dependencies']['nextcloud']['@attributes']['max-version']) ) { $this->emit('InfoChecker', 'missingRequirement', ['max']); } foreach ($info as $key => $value) { if(is_array($value)) { $value = json_encode($value); } if (in_array($key, $this->mandatoryFields)) { $this->emit('InfoChecker', 'mandatoryFieldFound', [$key, $value]); continue; } if (in_array($key, $this->optionalFields)) { $this->emit('InfoChecker', 'optionalFieldFound', [$key, $value]); continue; } if (in_array($key, $this->deprecatedFields)) { // skip empty arrays - empty arrays for remote and public are always added if($value === '[]' && in_array($key, ['public', 'remote', 'info'])) { continue; } $this->emit('InfoChecker', 'deprecatedFieldFound', [$key, $value]); continue; } $this->emit('InfoChecker', 'unusedFieldFound', [$key, $value]); } foreach ($this->mandatoryFields as $key) { if(!isset($info[$key])) { $this->emit('InfoChecker', 'mandatoryFieldMissing', [$key]); $errors[] = [ 'type' => 'mandatoryFieldMissing', 'field' => $key, ]; } } $versionFile = $appPath . '/appinfo/version'; if (is_file($versionFile)) { $version = trim(file_get_contents($versionFile)); if (isset($info['version'])) { if($info['version'] !== $version) { $this->emit('InfoChecker', 'differentVersions', [$version, $info['version']]); $errors[] = [ 'type' => 'differentVersions', 'message' => 'appinfo/version: ' . $version . ' - appinfo/info.xml: ' . $info['version'], ]; } else { $this->emit('InfoChecker', 'sameVersions', [$versionFile]); } } else { $this->emit('InfoChecker', 'migrateVersion', [$version]); } } return $errors; } } private/App/CodeChecker/NodeVisitor.php 0000604 00000024670 15247130451 0014062 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; use PhpParser\Node; use PhpParser\Node\Name; use PhpParser\NodeVisitorAbstract; class NodeVisitor extends NodeVisitorAbstract { /** @var ICheck */ protected $list; /** @var string */ protected $blackListDescription; /** @var string[] */ protected $blackListedClassNames; /** @var string[] */ protected $blackListedConstants; /** @var string[] */ protected $blackListedFunctions; /** @var string[] */ protected $blackListedMethods; /** @var bool */ protected $checkEqualOperatorUsage; /** @var string[] */ protected $errorMessages; /** * @param ICheck $list */ public function __construct(ICheck $list) { $this->list = $list; $this->blackListedClassNames = []; foreach ($list->getClasses() as $class => $blackListInfo) { if (is_numeric($class) && is_string($blackListInfo)) { $class = $blackListInfo; $blackListInfo = null; } $class = strtolower($class); $this->blackListedClassNames[$class] = $class; } $this->blackListedConstants = []; foreach ($list->getConstants() as $constantName => $blackListInfo) { $constantName = strtolower($constantName); $this->blackListedConstants[$constantName] = $constantName; } $this->blackListedFunctions = []; foreach ($list->getFunctions() as $functionName => $blackListInfo) { $functionName = strtolower($functionName); $this->blackListedFunctions[$functionName] = $functionName; } $this->blackListedMethods = []; foreach ($list->getMethods() as $functionName => $blackListInfo) { $functionName = strtolower($functionName); $this->blackListedMethods[$functionName] = $functionName; } $this->checkEqualOperatorUsage = $list->checkStrongComparisons(); $this->errorMessages = [ CodeChecker::CLASS_EXTENDS_NOT_ALLOWED => "%s class must not be extended", CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED => "%s interface must not be implemented", CodeChecker::STATIC_CALL_NOT_ALLOWED => "Static method of %s class must not be called", CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED => "Constant of %s class must not not be fetched", CodeChecker::CLASS_NEW_NOT_ALLOWED => "%s class must not be instantiated", CodeChecker::CLASS_USE_NOT_ALLOWED => "%s class must not be imported with a use statement", CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED => "Method of %s class must not be called", CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED => "is discouraged", ]; } /** @var array */ public $errors = []; public function enterNode(Node $node) { if ($this->checkEqualOperatorUsage && $node instanceof Node\Expr\BinaryOp\Equal) { $this->errors[]= [ 'disallowedToken' => '==', 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED, 'line' => $node->getLine(), 'reason' => $this->buildReason('==', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED) ]; } if ($this->checkEqualOperatorUsage && $node instanceof Node\Expr\BinaryOp\NotEqual) { $this->errors[]= [ 'disallowedToken' => '!=', 'errorCode' => CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED, 'line' => $node->getLine(), 'reason' => $this->buildReason('!=', CodeChecker::OP_OPERATOR_USAGE_DISCOURAGED) ]; } if ($node instanceof Node\Stmt\Class_) { if (!is_null($node->extends)) { $this->checkBlackList($node->extends->toString(), CodeChecker::CLASS_EXTENDS_NOT_ALLOWED, $node); } foreach ($node->implements as $implements) { $this->checkBlackList($implements->toString(), CodeChecker::CLASS_IMPLEMENTS_NOT_ALLOWED, $node); } } if ($node instanceof Node\Expr\StaticCall) { if (!is_null($node->class)) { if ($node->class instanceof Name) { $this->checkBlackList($node->class->toString(), CodeChecker::STATIC_CALL_NOT_ALLOWED, $node); $this->checkBlackListFunction($node->class->toString(), $node->name, $node); $this->checkBlackListMethod($node->class->toString(), $node->name, $node); } if ($node->class instanceof Node\Expr\Variable) { /** * TODO: find a way to detect something like this: * $c = "OC_API"; * $n = $c::call(); */ // $this->checkBlackListMethod($node->class->..., $node->name, $node); } } } if ($node instanceof Node\Expr\MethodCall) { if (!is_null($node->var)) { if ($node->var instanceof Node\Expr\Variable) { /** * TODO: find a way to detect something like this: * $c = new OC_API(); * $n = $c::call(); * $n = $c->call(); */ // $this->checkBlackListMethod($node->var->..., $node->name, $node); } } } if ($node instanceof Node\Expr\ClassConstFetch) { if (!is_null($node->class)) { if ($node->class instanceof Name) { $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED, $node); } if ($node->class instanceof Node\Expr\Variable) { /** * TODO: find a way to detect something like this: * $c = "OC_API"; * $n = $i::ADMIN_AUTH; */ } else { $this->checkBlackListConstant($node->class->toString(), $node->name, $node); } } } if ($node instanceof Node\Expr\New_) { if (!is_null($node->class)) { if ($node->class instanceof Name) { $this->checkBlackList($node->class->toString(), CodeChecker::CLASS_NEW_NOT_ALLOWED, $node); } if ($node->class instanceof Node\Expr\Variable) { /** * TODO: find a way to detect something like this: * $c = "OC_API"; * $n = new $i; */ } } } if ($node instanceof Node\Stmt\UseUse) { $this->checkBlackList($node->name->toString(), CodeChecker::CLASS_USE_NOT_ALLOWED, $node); if ($node->alias) { $this->addUseNameToBlackList($node->name->toString(), $node->alias); } else { $this->addUseNameToBlackList($node->name->toString(), $node->name->getLast()); } } } /** * Check whether an alias was introduced for a namespace of a blacklisted class * * Example: * - Blacklist entry: OCP\AppFramework\IApi * - Name: OCP\AppFramework * - Alias: OAF * => new blacklist entry: OAF\IApi * * @param string $name * @param string $alias */ private function addUseNameToBlackList($name, $alias) { $name = strtolower($name); $alias = strtolower($alias); foreach ($this->blackListedClassNames as $blackListedAlias => $blackListedClassName) { if (strpos($blackListedClassName, $name . '\\') === 0) { $aliasedClassName = str_replace($name, $alias, $blackListedClassName); $this->blackListedClassNames[$aliasedClassName] = $blackListedClassName; } } foreach ($this->blackListedConstants as $blackListedAlias => $blackListedConstant) { if (strpos($blackListedConstant, $name . '\\') === 0 || strpos($blackListedConstant, $name . '::') === 0) { $aliasedConstantName = str_replace($name, $alias, $blackListedConstant); $this->blackListedConstants[$aliasedConstantName] = $blackListedConstant; } } foreach ($this->blackListedFunctions as $blackListedAlias => $blackListedFunction) { if (strpos($blackListedFunction, $name . '\\') === 0 || strpos($blackListedFunction, $name . '::') === 0) { $aliasedFunctionName = str_replace($name, $alias, $blackListedFunction); $this->blackListedFunctions[$aliasedFunctionName] = $blackListedFunction; } } foreach ($this->blackListedMethods as $blackListedAlias => $blackListedMethod) { if (strpos($blackListedMethod, $name . '\\') === 0 || strpos($blackListedMethod, $name . '::') === 0) { $aliasedMethodName = str_replace($name, $alias, $blackListedMethod); $this->blackListedMethods[$aliasedMethodName] = $blackListedMethod; } } } private function checkBlackList($name, $errorCode, Node $node) { $lowerName = strtolower($name); if (isset($this->blackListedClassNames[$lowerName])) { $this->errors[]= [ 'disallowedToken' => $name, 'errorCode' => $errorCode, 'line' => $node->getLine(), 'reason' => $this->buildReason($this->blackListedClassNames[$lowerName], $errorCode) ]; } } private function checkBlackListConstant($class, $constantName, Node $node) { $name = $class . '::' . $constantName; $lowerName = strtolower($name); if (isset($this->blackListedConstants[$lowerName])) { $this->errors[]= [ 'disallowedToken' => $name, 'errorCode' => CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED, 'line' => $node->getLine(), 'reason' => $this->buildReason($this->blackListedConstants[$lowerName], CodeChecker::CLASS_CONST_FETCH_NOT_ALLOWED) ]; } } private function checkBlackListFunction($class, $functionName, Node $node) { $name = $class . '::' . $functionName; $lowerName = strtolower($name); if (isset($this->blackListedFunctions[$lowerName])) { $this->errors[]= [ 'disallowedToken' => $name, 'errorCode' => CodeChecker::STATIC_CALL_NOT_ALLOWED, 'line' => $node->getLine(), 'reason' => $this->buildReason($this->blackListedFunctions[$lowerName], CodeChecker::STATIC_CALL_NOT_ALLOWED) ]; } } private function checkBlackListMethod($class, $functionName, Node $node) { $name = $class . '::' . $functionName; $lowerName = strtolower($name); if (isset($this->blackListedMethods[$lowerName])) { $this->errors[]= [ 'disallowedToken' => $name, 'errorCode' => CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED, 'line' => $node->getLine(), 'reason' => $this->buildReason($this->blackListedMethods[$lowerName], CodeChecker::CLASS_METHOD_CALL_NOT_ALLOWED) ]; } } private function buildReason($name, $errorCode) { if (isset($this->errorMessages[$errorCode])) { $desc = $this->list->getDescription($errorCode, $name); return sprintf($this->errorMessages[$errorCode], $desc); } return "$name usage not allowed - error: $errorCode"; } } private/App/CodeChecker/DeprecationCheck.php 0000604 00000011656 15247130451 0015010 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; class DeprecationCheck extends AbstractCheck implements ICheck { /** * @return string */ protected function getLocalDescription() { return 'deprecated'; } /** * @return array E.g.: `'ClassName' => 'oc version',` */ protected function getLocalClasses() { return [ 'OC_JSON' => '8.2.0', 'OCP\Config' => '8.0.0', 'OCP\Contacts' => '8.1.0', 'OCP\DB' => '8.1.0', 'OCP\IHelper' => '8.1.0', 'OCP\JSON' => '8.1.0', 'OCP\Response' => '8.1.0', 'OCP\AppFramework\IApi' => '8.0.0', ]; } /** * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',` */ protected function getLocalConstants() { return [ 'OC_API::GUEST_AUTH' => '8.2.0', 'OC_API::USER_AUTH' => '8.2.0', 'OC_API::SUBADMIN_AUTH' => '8.2.0', 'OC_API::ADMIN_AUTH' => '8.2.0', 'OC_API::RESPOND_UNAUTHORISED' => '8.2.0', 'OC_API::RESPOND_SERVER_ERROR' => '8.2.0', 'OC_API::RESPOND_NOT_FOUND' => '8.2.0', 'OC_API::RESPOND_UNKNOWN_ERROR' => '8.2.0', 'OCP::PERMISSION_CREATE' => '8.0.0', 'OCP::PERMISSION_READ' => '8.0.0', 'OCP::PERMISSION_UPDATE' => '8.0.0', 'OCP::PERMISSION_DELETE' => '8.0.0', 'OCP::PERMISSION_SHARE' => '8.0.0', 'OCP::PERMISSION_ALL' => '8.0.0', 'OCP::FILENAME_INVALID_CHARS' => '8.0.0', ]; } /** * @return array E.g.: `'functionName' => 'oc version',` */ protected function getLocalFunctions() { return [ 'OCP::image_path' => '8.0.0', 'OCP::mimetype_icon' => '8.0.0', 'OCP::preview_icon' => '8.0.0', 'OCP::publicPreview_icon' => '8.0.0', 'OCP::human_file_size' => '8.0.0', 'OCP::relative_modified_date' => '8.0.0', 'OCP::simple_file_size' => '8.0.0', 'OCP::html_select_options' => '8.0.0', ]; } /** * @return array E.g.: `'ClassName::methodName' => 'oc version',` */ protected function getLocalMethods() { return [ 'OC_L10N::get' => '8.2.0', 'OCP\Activity\IManager::publishActivity' => '8.2.0', 'OCP\App::register' => '8.1.0', 'OCP\App::addNavigationEntry' => '8.1.0', 'OCP\App::getActiveNavigationEntry' => '8.2.0', 'OCP\App::setActiveNavigationEntry' => '8.1.0', 'OCP\AppFramework\Controller::params' => '7.0.0', 'OCP\AppFramework\Controller::getParams' => '7.0.0', 'OCP\AppFramework\Controller::method' => '7.0.0', 'OCP\AppFramework\Controller::getUploadedFile' => '7.0.0', 'OCP\AppFramework\Controller::env' => '7.0.0', 'OCP\AppFramework\Controller::cookie' => '7.0.0', 'OCP\AppFramework\Controller::render' => '7.0.0', 'OCP\AppFramework\IAppContainer::getCoreApi' => '8.0.0', 'OCP\AppFramework\IAppContainer::isLoggedIn' => '8.0.0', 'OCP\AppFramework\IAppContainer::isAdminUser' => '8.0.0', 'OCP\AppFramework\IAppContainer::log' => '8.0.0', 'OCP\BackgroundJob::registerJob' => '8.1.0', 'OCP\Files::tmpFile' => '8.1.0', 'OCP\Files::tmpFolder' => '8.1.0', 'OCP\IAppConfig::getValue' => '8.0.0', 'OCP\IAppConfig::deleteKey' => '8.0.0', 'OCP\IAppConfig::getKeys' => '8.0.0', 'OCP\IAppConfig::setValue' => '8.0.0', 'OCP\IAppConfig::deleteApp' => '8.0.0', 'OCP\IDBConnection::createQueryBuilder' => '8.2.0', 'OCP\IDBConnection::getExpressionBuilder' => '8.2.0', 'OCP\ISearch::search' => '8.0.0', 'OCP\IServerContainer::getCache' => '8.2.0', 'OCP\IServerContainer::getDb' => '8.1.0', 'OCP\IServerContainer::getHTTPHelper' => '8.1.0', 'OCP\User::getUser' => '8.0.0', 'OCP\User::getUsers' => '8.1.0', 'OCP\User::getDisplayName' => '8.1.0', 'OCP\User::getDisplayNames' => '8.1.0', 'OCP\User::userExists' => '8.1.0', 'OCP\User::logout' => '8.1.0', 'OCP\User::checkPassword' => '8.1.0', 'OCP\Util::encryptedFiles' => '8.1.0', 'OCP\Util::formatDate' => '8.0.0', 'OCP\Util::generateRandomBytes' => '8.1.0', 'OCP\Util::getServerHost' => '8.1.0', 'OCP\Util::getServerProtocol' => '8.1.0', 'OCP\Util::getRequestUri' => '8.1.0', 'OCP\Util::getScriptName' => '8.1.0', 'OCP\Util::imagePath' => '8.1.0', 'OCP\Util::isValidFileName' => '8.1.0', 'OCP\Util::linkToRoute' => '8.1.0', 'OCP\Util::linkTo' => '8.1.0', 'OCP\Util::logException' => '8.2.0', 'OCP\Util::mb_str_replace' => '8.2.0', 'OCP\Util::mb_substr_replace' => '8.2.0', 'OCP\Util::sendMail' => '8.1.0', ]; } } private/App/CodeChecker/LanguageParseChecker.php 0000604 00000003205 15247130451 0015607 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\CodeChecker; class LanguageParseChecker { /** * @param string $appId * @return array */ public function analyse($appId) { $appPath = \OC_App::getAppPath($appId); if ($appPath === false) { throw new \RuntimeException("No app with given id <$appId> known."); } if (!is_dir($appPath . '/l10n/')) { return []; } $errors = []; $directory = new \DirectoryIterator($appPath . '/l10n/'); foreach ($directory as $file) { if ($file->getExtension() !== 'json') { continue; } $content = file_get_contents($file->getPathname()); json_decode($content, true); if (json_last_error() !== JSON_ERROR_NONE) { $errors[] = 'Invalid language file found: l10n/' . $file->getFilename() . ': ' . json_last_error_msg(); } } return $errors; } } private/App/CodeChecker/CodeChecker.php 0000604 00000007072 15247130451 0013751 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; use OC\Hooks\BasicEmitter; use PhpParser\Lexer; use PhpParser\NodeTraverser; use PhpParser\Parser; use RecursiveCallbackFilterIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use RegexIterator; use SplFileInfo; class CodeChecker extends BasicEmitter { const CLASS_EXTENDS_NOT_ALLOWED = 1000; const CLASS_IMPLEMENTS_NOT_ALLOWED = 1001; const STATIC_CALL_NOT_ALLOWED = 1002; const CLASS_CONST_FETCH_NOT_ALLOWED = 1003; const CLASS_NEW_NOT_ALLOWED = 1004; const OP_OPERATOR_USAGE_DISCOURAGED = 1005; const CLASS_USE_NOT_ALLOWED = 1006; const CLASS_METHOD_CALL_NOT_ALLOWED = 1007; /** @var Parser */ private $parser; /** @var ICheck */ protected $checkList; public function __construct(ICheck $checkList) { $this->checkList = $checkList; $this->parser = new Parser(new Lexer); } /** * @param string $appId * @return array */ public function analyse($appId) { $appPath = \OC_App::getAppPath($appId); if ($appPath === false) { throw new \RuntimeException("No app with given id <$appId> known."); } return $this->analyseFolder($appId, $appPath); } /** * @param string $appId * @param string $folder * @return array */ public function analyseFolder($appId, $folder) { $errors = []; $excludedDirectories = ['vendor', '3rdparty', '.git', 'l10n', 'tests', 'test']; if ($appId === 'password_policy') { $excludedDirectories[] = 'lists'; } $excludes = array_map(function($item) use ($folder) { return $folder . '/' . $item; }, $excludedDirectories); $iterator = new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator::SKIP_DOTS); $iterator = new RecursiveCallbackFilterIterator($iterator, function($item) use ($folder, $excludes){ /** @var SplFileInfo $item */ foreach($excludes as $exclude) { if (substr($item->getPath(), 0, strlen($exclude)) === $exclude) { return false; } } return true; }); $iterator = new RecursiveIteratorIterator($iterator); $iterator = new RegexIterator($iterator, '/^.+\.php$/i'); foreach ($iterator as $file) { /** @var SplFileInfo $file */ $this->emit('CodeChecker', 'analyseFileBegin', [$file->getPathname()]); $fileErrors = $this->analyseFile($file); $this->emit('CodeChecker', 'analyseFileFinished', [$file->getPathname(), $fileErrors]); $errors = array_merge($fileErrors, $errors); } return $errors; } /** * @param string $file * @return array */ public function analyseFile($file) { $code = file_get_contents($file); $statements = $this->parser->parse($code); $visitor = new NodeVisitor($this->checkList); $traverser = new NodeTraverser; $traverser->addVisitor($visitor); $traverser->traverse($statements); return $visitor->errors; } } private/App/CodeChecker/EmptyCheck.php 0000604 00000003040 15247130451 0013635 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; class EmptyCheck implements ICheck { /** * @param int $errorCode * @param string $errorObject * @return string */ public function getDescription($errorCode, $errorObject) { return ''; } /** * @return array E.g.: `'ClassName' => 'oc version',` */ public function getClasses() { return []; } /** * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',` */ public function getConstants() { return []; } /** * @return array E.g.: `'functionName' => 'oc version',` */ public function getFunctions() { return []; } /** * @return array E.g.: `'ClassName::methodName' => 'oc version',` */ public function getMethods() { return []; } /** * @return bool */ public function checkStrongComparisons() { return false; } } private/App/CodeChecker/StrongComparisonCheck.php 0000604 00000003247 15247130451 0016057 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; class StrongComparisonCheck implements ICheck { /** @var ICheck */ protected $check; /** * @param ICheck $check */ public function __construct(ICheck $check) { $this->check = $check; } /** * @param int $errorCode * @param string $errorObject * @return string */ public function getDescription($errorCode, $errorObject) { return $this->check->getDescription($errorCode, $errorObject); } /** * @return array */ public function getClasses() { return $this->check->getClasses(); } /** * @return array */ public function getConstants() { return $this->check->getConstants(); } /** * @return array */ public function getFunctions() { return $this->check->getFunctions(); } /** * @return array */ public function getMethods() { return $this->check->getMethods(); } /** * @return bool */ public function checkStrongComparisons() { return true; } } private/App/CodeChecker/ICheck.php 0000604 00000002645 15247130451 0012741 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App\CodeChecker; interface ICheck { /** * @param int $errorCode * @param string $errorObject * @return string */ public function getDescription($errorCode, $errorObject); /** * @return array E.g.: `'ClassName' => 'oc version',` */ public function getClasses(); /** * @return array E.g.: `'ClassName::CONSTANT_NAME' => 'oc version',` */ public function getConstants(); /** * @return array E.g.: `'functionName' => 'oc version',` */ public function getFunctions(); /** * @return array E.g.: `'ClassName::methodName' => 'oc version',` */ public function getMethods(); /** * @return bool */ public function checkStrongComparisons(); } private/App/AppManager.php 0000604 00000025016 15247130451 0011464 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Christoph Schaefer <christophł@wolkesicher.de> * @author Christoph Wurst <christoph@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App; use OCP\App\AppPathNotFoundException; use OCP\App\IAppManager; use OCP\App\ManagerEvent; use OCP\IAppConfig; use OCP\ICacheFactory; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserSession; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class AppManager implements IAppManager { /** * Apps with these types can not be enabled for certain groups only * @var string[] */ protected $protectedAppTypes = [ 'filesystem', 'prelogin', 'authentication', 'logging', 'prevent_group_restriction', ]; /** @var IUserSession */ private $userSession; /** @var IAppConfig */ private $appConfig; /** @var IGroupManager */ private $groupManager; /** @var ICacheFactory */ private $memCacheFactory; /** @var EventDispatcherInterface */ private $dispatcher; /** @var string[] $appId => $enabled */ private $installedAppsCache; /** @var string[] */ private $shippedApps; /** @var string[] */ private $alwaysEnabled; /** * @param IUserSession $userSession * @param IAppConfig $appConfig * @param IGroupManager $groupManager * @param ICacheFactory $memCacheFactory * @param EventDispatcherInterface $dispatcher */ public function __construct(IUserSession $userSession, IAppConfig $appConfig, IGroupManager $groupManager, ICacheFactory $memCacheFactory, EventDispatcherInterface $dispatcher) { $this->userSession = $userSession; $this->appConfig = $appConfig; $this->groupManager = $groupManager; $this->memCacheFactory = $memCacheFactory; $this->dispatcher = $dispatcher; } /** * @return string[] $appId => $enabled */ private function getInstalledAppsValues() { if (!$this->installedAppsCache) { $values = $this->appConfig->getValues(false, 'enabled'); $alwaysEnabledApps = $this->getAlwaysEnabledApps(); foreach($alwaysEnabledApps as $appId) { $values[$appId] = 'yes'; } $this->installedAppsCache = array_filter($values, function ($value) { return $value !== 'no'; }); ksort($this->installedAppsCache); } return $this->installedAppsCache; } /** * List all installed apps * * @return string[] */ public function getInstalledApps() { return array_keys($this->getInstalledAppsValues()); } /** * List all apps enabled for a user * * @param \OCP\IUser $user * @return string[] */ public function getEnabledAppsForUser(IUser $user) { $apps = $this->getInstalledAppsValues(); $appsForUser = array_filter($apps, function ($enabled) use ($user) { return $this->checkAppForUser($enabled, $user); }); return array_keys($appsForUser); } /** * Check if an app is enabled for user * * @param string $appId * @param \OCP\IUser $user (optional) if not defined, the currently logged in user will be used * @return bool */ public function isEnabledForUser($appId, $user = null) { if ($this->isAlwaysEnabled($appId)) { return true; } if ($user === null) { $user = $this->userSession->getUser(); } $installedApps = $this->getInstalledAppsValues(); if (isset($installedApps[$appId])) { return $this->checkAppForUser($installedApps[$appId], $user); } else { return false; } } /** * @param string $enabled * @param IUser $user * @return bool */ private function checkAppForUser($enabled, $user) { if ($enabled === 'yes') { return true; } elseif ($user === null) { return false; } else { if(empty($enabled)){ return false; } $groupIds = json_decode($enabled); if (!is_array($groupIds)) { $jsonError = json_last_error(); \OC::$server->getLogger()->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']); return false; } $userGroups = $this->groupManager->getUserGroupIds($user); foreach ($userGroups as $groupId) { if (in_array($groupId, $groupIds, true)) { return true; } } return false; } } /** * Check if an app is installed in the instance * * @param string $appId * @return bool */ public function isInstalled($appId) { $installedApps = $this->getInstalledAppsValues(); return isset($installedApps[$appId]); } /** * Enable an app for every user * * @param string $appId * @throws AppPathNotFoundException */ public function enableApp($appId) { // Check if app exists $this->getAppPath($appId); $this->installedAppsCache[$appId] = 'yes'; $this->appConfig->setValue($appId, 'enabled', 'yes'); $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent( ManagerEvent::EVENT_APP_ENABLE, $appId )); $this->clearAppsCache(); } /** * Whether a list of types contains a protected app type * * @param string[] $types * @return bool */ public function hasProtectedAppType($types) { if (empty($types)) { return false; } $protectedTypes = array_intersect($this->protectedAppTypes, $types); return !empty($protectedTypes); } /** * Enable an app only for specific groups * * @param string $appId * @param \OCP\IGroup[] $groups * @throws \Exception if app can't be enabled for groups */ public function enableAppForGroups($appId, $groups) { $info = $this->getAppInfo($appId); if (!empty($info['types'])) { $protectedTypes = array_intersect($this->protectedAppTypes, $info['types']); if (!empty($protectedTypes)) { throw new \Exception("$appId can't be enabled for groups."); } } $groupIds = array_map(function ($group) { /** @var \OCP\IGroup $group */ return $group->getGID(); }, $groups); $this->installedAppsCache[$appId] = json_encode($groupIds); $this->appConfig->setValue($appId, 'enabled', json_encode($groupIds)); $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent( ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups )); $this->clearAppsCache(); } /** * Disable an app for every user * * @param string $appId * @throws \Exception if app can't be disabled */ public function disableApp($appId) { if ($this->isAlwaysEnabled($appId)) { throw new \Exception("$appId can't be disabled."); } unset($this->installedAppsCache[$appId]); $this->appConfig->setValue($appId, 'enabled', 'no'); $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent( ManagerEvent::EVENT_APP_DISABLE, $appId )); $this->clearAppsCache(); } /** * Get the directory for the given app. * * @param string $appId * @return string * @throws AppPathNotFoundException if app folder can't be found */ public function getAppPath($appId) { $appPath = \OC_App::getAppPath($appId); if($appPath === false) { throw new AppPathNotFoundException('Could not find path for ' . $appId); } return $appPath; } /** * Clear the cached list of apps when enabling/disabling an app */ public function clearAppsCache() { $settingsMemCache = $this->memCacheFactory->create('settings'); $settingsMemCache->clear('listApps'); } /** * Returns a list of apps that need upgrade * * @param string $version Nextcloud version as array of version components * @return array list of app info from apps that need an upgrade * * @internal */ public function getAppsNeedingUpgrade($version) { $appsToUpgrade = []; $apps = $this->getInstalledApps(); foreach ($apps as $appId) { $appInfo = $this->getAppInfo($appId); $appDbVersion = $this->appConfig->getValue($appId, 'installed_version'); if ($appDbVersion && isset($appInfo['version']) && version_compare($appInfo['version'], $appDbVersion, '>') && \OC_App::isAppCompatible($version, $appInfo) ) { $appsToUpgrade[] = $appInfo; } } return $appsToUpgrade; } /** * Returns the app information from "appinfo/info.xml". * * @param string $appId app id * * @return array app info * * @internal */ public function getAppInfo($appId) { $appInfo = \OC_App::getAppInfo($appId); if (!isset($appInfo['version'])) { // read version from separate file $appInfo['version'] = \OC_App::getAppVersion($appId); } return $appInfo; } /** * Returns a list of apps incompatible with the given version * * @param string $version Nextcloud version as array of version components * * @return array list of app info from incompatible apps * * @internal */ public function getIncompatibleApps($version) { $apps = $this->getInstalledApps(); $incompatibleApps = array(); foreach ($apps as $appId) { $info = $this->getAppInfo($appId); if (!\OC_App::isAppCompatible($version, $info)) { $incompatibleApps[] = $info; } } return $incompatibleApps; } /** * @inheritdoc */ public function isShipped($appId) { $this->loadShippedJson(); return in_array($appId, $this->shippedApps, true); } private function isAlwaysEnabled($appId) { $alwaysEnabled = $this->getAlwaysEnabledApps(); return in_array($appId, $alwaysEnabled, true); } private function loadShippedJson() { if ($this->shippedApps === null) { $shippedJson = \OC::$SERVERROOT . '/core/shipped.json'; if (!file_exists($shippedJson)) { throw new \Exception("File not found: $shippedJson"); } $content = json_decode(file_get_contents($shippedJson), true); $this->shippedApps = $content['shippedApps']; $this->alwaysEnabled = $content['alwaysEnabled']; } } /** * @inheritdoc */ public function getAlwaysEnabledApps() { $this->loadShippedJson(); return $this->alwaysEnabled; } } private/App/PlatformRepository.php 0000604 00000015136 15247130451 0013337 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App; /** * Class PlatformRepository * * Inspired by the composer project - licensed under MIT * https://github.com/composer/composer/blob/master/src/Composer/Repository/PlatformRepository.php#L82 * * @package OC\App */ class PlatformRepository { public function __construct() { $this->packages = $this->initialize(); } protected function initialize() { $loadedExtensions = get_loaded_extensions(); $packages = array(); // Extensions scanning foreach ($loadedExtensions as $name) { if (in_array($name, array('standard', 'Core'))) { continue; } $ext = new \ReflectionExtension($name); try { $prettyVersion = $ext->getVersion(); $prettyVersion = $this->normalizeVersion($prettyVersion); } catch (\UnexpectedValueException $e) { $prettyVersion = '0'; $prettyVersion = $this->normalizeVersion($prettyVersion); } $packages[$this->buildPackageName($name)] = $prettyVersion; } foreach ($loadedExtensions as $name) { $prettyVersion = null; switch ($name) { case 'curl': $curlVersion = curl_version(); $prettyVersion = $curlVersion['version']; break; case 'iconv': $prettyVersion = ICONV_VERSION; break; case 'intl': $name = 'ICU'; if (defined('INTL_ICU_VERSION')) { $prettyVersion = INTL_ICU_VERSION; } else { $reflector = new \ReflectionExtension('intl'); ob_start(); $reflector->info(); $output = ob_get_clean(); preg_match('/^ICU version => (.*)$/m', $output, $matches); $prettyVersion = $matches[1]; } break; case 'libxml': $prettyVersion = LIBXML_DOTTED_VERSION; break; case 'openssl': $prettyVersion = preg_replace_callback('{^(?:OpenSSL\s*)?([0-9.]+)([a-z]?).*}', function ($match) { return $match[1] . (empty($match[2]) ? '' : '.' . (ord($match[2]) - 96)); }, OPENSSL_VERSION_TEXT); break; case 'pcre': $prettyVersion = preg_replace('{^(\S+).*}', '$1', PCRE_VERSION); break; case 'uuid': $prettyVersion = phpversion('uuid'); break; case 'xsl': $prettyVersion = LIBXSLT_DOTTED_VERSION; break; default: // None handled extensions have no special cases, skip continue 2; } try { $prettyVersion = $this->normalizeVersion($prettyVersion); } catch (\UnexpectedValueException $e) { continue; } $packages[$this->buildPackageName($name)] = $prettyVersion; } return $packages; } private function buildPackageName($name) { return str_replace(' ', '-', $name); } /** * @param $name * @return string */ public function findLibrary($name) { $extName = $this->buildPackageName($name); if (isset($this->packages[$extName])) { return $this->packages[$extName]; } return null; } private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?'; /** * Normalizes a version string to be able to perform comparisons on it * * https://github.com/composer/composer/blob/master/src/Composer/Package/Version/VersionParser.php#L94 * * @param string $version * @param string $fullVersion optional complete version string to give more context * @throws \UnexpectedValueException * @return string */ public function normalizeVersion($version, $fullVersion = null) { $version = trim($version); if (null === $fullVersion) { $fullVersion = $version; } // ignore aliases and just assume the alias is required instead of the source if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $version, $match)) { $version = $match[1]; } // match master-like branches if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) { return '9999999-dev'; } if ('dev-' === strtolower(substr($version, 0, 4))) { return 'dev-' . substr($version, 4); } // match classical versioning if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?' . self::$modifierRegex . '$}i', $version, $matches)) { $version = $matches[1] . (!empty($matches[2]) ? $matches[2] : '.0') . (!empty($matches[3]) ? $matches[3] : '.0') . (!empty($matches[4]) ? $matches[4] : '.0'); $index = 5; } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) { // match date-based versioning $version = preg_replace('{\D}', '-', $matches[1]); $index = 2; } elseif (preg_match('{^v?(\d{4,})(\.\d+)?(\.\d+)?(\.\d+)?' . self::$modifierRegex . '$}i', $version, $matches)) { $version = $matches[1] . (!empty($matches[2]) ? $matches[2] : '.0') . (!empty($matches[3]) ? $matches[3] : '.0') . (!empty($matches[4]) ? $matches[4] : '.0'); $index = 5; } // add version modifiers if a version was matched if (isset($index)) { if (!empty($matches[$index])) { if ('stable' === $matches[$index]) { return $version; } $version .= '-' . $this->expandStability($matches[$index]) . (!empty($matches[$index + 1]) ? $matches[$index + 1] : ''); } if (!empty($matches[$index + 2])) { $version .= '-dev'; } return $version; } $extraMessage = ''; if (preg_match('{ +as +' . preg_quote($version) . '$}', $fullVersion)) { $extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version'; } elseif (preg_match('{^' . preg_quote($version) . ' +as +}', $fullVersion)) { $extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-'; } throw new \UnexpectedValueException('Invalid version string "' . $version . '"' . $extraMessage); } /** * @param string $stability */ private function expandStability($stability) { $stability = strtolower($stability); switch ($stability) { case 'a': return 'alpha'; case 'b': return 'beta'; case 'p': case 'pl': return 'patch'; case 'rc': return 'RC'; default: return $stability; } } } private/App/AppStore/Version/VersionParser.php 0000604 00000004766 15247130451 0015446 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Version; /** * Class VersionParser parses the versions as sent by the Nextcloud app store * * @package OC\App\AppStore */ class VersionParser { /** * @param string $versionString * @return bool */ private function isValidVersionString($versionString) { return (bool)preg_match('/^[0-9.]+$/', $versionString); } /** * Returns the version for a version string * * @param string $versionSpec * @return Version * @throws \Exception If the version cannot be parsed */ public function getVersion($versionSpec) { // * indicates that the version is compatible with all versions if($versionSpec === '*') { return new Version('', ''); } // Count the amount of =, if it is one then it's either maximum or minimum // version. If it is two then it is maximum and minimum. $versionElements = explode(' ', $versionSpec); $firstVersion = isset($versionElements[0]) ? $versionElements[0] : ''; $firstVersionNumber = substr($firstVersion, 2); $secondVersion = isset($versionElements[1]) ? $versionElements[1] : ''; $secondVersionNumber = substr($secondVersion, 2); switch(count($versionElements)) { case 1: if(!$this->isValidVersionString($firstVersionNumber)) { break; } if(substr($firstVersion, 0, 1) === '>') { return new Version($firstVersionNumber, ''); } else { return new Version('', $firstVersionNumber); } case 2: if(!$this->isValidVersionString($firstVersionNumber) || !$this->isValidVersionString($secondVersionNumber)) { break; } return new Version($firstVersionNumber, $secondVersionNumber); } throw new \Exception( sprintf( 'Version cannot be parsed: %s', $versionSpec ) ); } } private/App/AppStore/Version/Version.php 0000604 00000002507 15247130451 0014260 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Version; class Version { /** @var string */ private $minVersion; /** @var string */ private $maxVersion; /** * @param string $minVersion * @param string $maxVersion */ public function __construct($minVersion, $maxVersion) { $this->minVersion = $minVersion; $this->maxVersion = $maxVersion; } /** * @return string */ public function getMinimumVersion() { return $this->minVersion; } /** * @return string */ public function getMaximumVersion() { return $this->maxVersion; } } private/App/AppStore/Bundles/BundleFetcher.php 0000604 00000003757 15247130451 0015324 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Bundles; use OCP\IL10N; class BundleFetcher { /** @var IL10N */ private $l10n; /** * @param IL10N $l10n */ public function __construct(IL10N $l10n) { $this->l10n = $l10n; } /** * @return Bundle[] */ public function getBundles() { return [ new EnterpriseBundle($this->l10n), new GroupwareBundle($this->l10n), new SocialSharingBundle($this->l10n), new EducationBundle($this->l10n), ]; } /** * Bundles that should be installed by default after installation * * @return Bundle[] */ public function getDefaultInstallationBundle() { return [ new CoreBundle($this->l10n), ]; } /** * Get the bundle with the specified identifier * * @param string $identifier * @return Bundle * @throws \BadMethodCallException If the bundle does not exist */ public function getBundleByIdentifier($identifier) { /** @var Bundle[] $bundles */ $bundles = array_merge( $this->getBundles(), $this->getDefaultInstallationBundle() ); foreach($bundles as $bundle) { if($bundle->getIdentifier() === $identifier) { return $bundle; } } throw new \BadMethodCallException('Bundle with specified identifier does not exist'); } } private/App/AppStore/Bundles/CoreBundle.php 0000604 00000002072 15247130451 0014621 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Bundles; class CoreBundle extends Bundle { /** * {@inheritDoc} */ public function getName() { return 'Core bundle'; } /** * {@inheritDoc} */ public function getAppIdentifiers() { return [ 'bruteforcesettings', ]; } } private/App/AppStore/Bundles/GroupwareBundle.php 0000604 00000002156 15247130451 0015707 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Bundles; class GroupwareBundle extends Bundle { /** * {@inheritDoc} */ public function getName() { return (string)$this->l10n->t('Groupware bundle'); } /** * {@inheritDoc} */ public function getAppIdentifiers() { return [ 'calendar', 'contacts', 'spreed', ]; } } private/App/AppStore/Bundles/Bundle.php 0000604 00000002627 15247130451 0014016 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Bundles; use OCP\IL10N; abstract class Bundle { /** @var IL10N */ protected $l10n; /** * @param IL10N $l10n */ public function __construct(IL10N $l10n) { $this->l10n = $l10n; } /** * Get the identifier of the bundle * * @return string */ public final function getIdentifier() { return substr(strrchr(get_class($this), '\\'), 1); } /** * Get the name of the bundle * * @return string */ public abstract function getName(); /** * Get the list of app identifiers in the bundle * * @return array */ public abstract function getAppIdentifiers(); } private/App/AppStore/Bundles/EducationBundle.php 0000604 00000002345 15247130451 0015647 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Bundles; class EducationBundle extends Bundle { /** * {@inheritDoc} */ public function getName() { return (string)$this->l10n->t('Education Edition'); } /** * {@inheritDoc} */ public function getAppIdentifiers() { return [ 'zenodo', 'dashboard', 'circles', 'groupfolders', 'announcementcenter', 'admin_notifications', 'quota_warning', 'orcid', 'user_saml', ]; } } private/App/AppStore/Bundles/SocialSharingBundle.php 0000604 00000002333 15247130451 0016457 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Bundles; class SocialSharingBundle extends Bundle { /** * {@inheritDoc} */ public function getName() { return (string)$this->l10n->t('Social sharing bundle'); } /** * {@inheritDoc} */ public function getAppIdentifiers() { return [ 'socialsharing_twitter', 'socialsharing_googleplus', 'socialsharing_facebook', 'socialsharing_email', 'socialsharing_diaspora', ]; } } private/App/AppStore/Bundles/EnterpriseBundle.php 0000604 00000002304 15247130451 0016047 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Bundles; class EnterpriseBundle extends Bundle { /** * {@inheritDoc} */ public function getName() { return (string)$this->l10n->t('Enterprise bundle'); } /** * {@inheritDoc} */ public function getAppIdentifiers() { return [ 'admin_audit', 'user_ldap', 'files_retention', 'files_automatedtagging', 'user_saml', 'files_accesscontrol', ]; } } private/App/AppStore/Fetcher/Fetcher.php 0000604 00000011556 15247130451 0014152 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Fetcher; use OC\Files\AppData\Factory; use GuzzleHttp\Exception\ConnectException; use OCP\AppFramework\Http; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\ILogger; abstract class Fetcher { const INVALIDATE_AFTER_SECONDS = 300; /** @var IAppData */ protected $appData; /** @var IClientService */ protected $clientService; /** @var ITimeFactory */ protected $timeFactory; /** @var IConfig */ protected $config; /** @var Ilogger */ protected $logger; /** @var string */ protected $fileName; /** @var string */ protected $endpointUrl; /** @var string */ protected $version; /** * @param Factory $appDataFactory * @param IClientService $clientService * @param ITimeFactory $timeFactory * @param IConfig $config * @param ILogger $logger */ public function __construct(Factory $appDataFactory, IClientService $clientService, ITimeFactory $timeFactory, IConfig $config, ILogger $logger) { $this->appData = $appDataFactory->get('appstore'); $this->clientService = $clientService; $this->timeFactory = $timeFactory; $this->config = $config; $this->logger = $logger; } /** * Fetches the response from the server * * @param string $ETag * @param string $content * * @return array */ protected function fetch($ETag, $content) { $appstoreenabled = $this->config->getSystemValue('appstoreenabled', true); if (!$appstoreenabled) { return []; } $options = [ 'timeout' => 10, ]; if ($ETag !== '') { $options['headers'] = [ 'If-None-Match' => $ETag, ]; } $client = $this->clientService->newClient(); $response = $client->get($this->endpointUrl, $options); $responseJson = []; if ($response->getStatusCode() === Http::STATUS_NOT_MODIFIED) { $responseJson['data'] = json_decode($content, true); } else { $responseJson['data'] = json_decode($response->getBody(), true); $ETag = $response->getHeader('ETag'); } $responseJson['timestamp'] = $this->timeFactory->getTime(); $responseJson['ncversion'] = $this->getVersion(); if ($ETag !== '') { $responseJson['ETag'] = $ETag; } return $responseJson; } /** * Returns the array with the categories on the appstore server * * @return array */ public function get() { $appstoreenabled = $this->config->getSystemValue('appstoreenabled', true); if (!$appstoreenabled) { return []; } $rootFolder = $this->appData->getFolder('/'); $ETag = ''; $content = ''; try { // File does already exists $file = $rootFolder->getFile($this->fileName); $jsonBlob = json_decode($file->getContent(), true); if (is_array($jsonBlob)) { // No caching when the version has been updated if (isset($jsonBlob['ncversion']) && $jsonBlob['ncversion'] === $this->getVersion()) { // If the timestamp is older than 300 seconds request the files new if ((int)$jsonBlob['timestamp'] > ($this->timeFactory->getTime() - self::INVALIDATE_AFTER_SECONDS)) { return $jsonBlob['data']; } if (isset($jsonBlob['ETag'])) { $ETag = $jsonBlob['ETag']; $content = json_encode($jsonBlob['data']); } } } } catch (NotFoundException $e) { // File does not already exists $file = $rootFolder->newFile($this->fileName); } // Refresh the file content try { $responseJson = $this->fetch($ETag, $content); $file->putContent(json_encode($responseJson)); return json_decode($file->getContent(), true)['data']; } catch (ConnectException $e) { $this->logger->logException($e, ['app' => 'appstoreFetcher']); return []; } catch (\Exception $e) { return []; } } /** * Get the currently Nextcloud version * @return string */ protected function getVersion() { if ($this->version === null) { $this->version = $this->config->getSystemValue('version', '0.0.0'); } return $this->version; } /** * Set the current Nextcloud version * @param string $version */ public function setVersion($version) { $this->version = $version; } } private/App/AppStore/Fetcher/CategoryFetcher.php 0000604 00000003120 15247130451 0015634 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Fetcher; use OC\Files\AppData\Factory; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\ILogger; class CategoryFetcher extends Fetcher { /** * @param Factory $appDataFactory * @param IClientService $clientService * @param ITimeFactory $timeFactory * @param IConfig $config * @param ILogger $logger */ public function __construct(Factory $appDataFactory, IClientService $clientService, ITimeFactory $timeFactory, IConfig $config, ILogger $logger) { parent::__construct( $appDataFactory, $clientService, $timeFactory, $config, $logger ); $this->fileName = 'categories.json'; $this->endpointUrl = 'https://apps.nextcloud.com/api/v1/categories.json'; } } private/App/AppStore/Fetcher/AppFetcher.php 0000604 00000007464 15247130451 0014616 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\App\AppStore\Fetcher; use OC\App\AppStore\Version\VersionParser; use OC\Files\AppData\Factory; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\ILogger; class AppFetcher extends Fetcher { /** * @param Factory $appDataFactory * @param IClientService $clientService * @param ITimeFactory $timeFactory * @param IConfig $config * @param ILogger $logger */ public function __construct(Factory $appDataFactory, IClientService $clientService, ITimeFactory $timeFactory, IConfig $config, ILogger $logger) { parent::__construct( $appDataFactory, $clientService, $timeFactory, $config, $logger ); $this->fileName = 'apps.json'; $this->setEndpoint(); } /** * Only returns the latest compatible app release in the releases array * * @param string $ETag * @param string $content * * @return array */ protected function fetch($ETag, $content) { /** @var mixed[] $response */ $response = parent::fetch($ETag, $content); $ncVersion = $this->getVersion(); $ncMajorVersion = explode('.', $ncVersion)[0]; foreach($response['data'] as $dataKey => $app) { $releases = []; // Filter all compatible releases foreach($app['releases'] as $release) { // Exclude all nightly and pre-releases if($release['isNightly'] === false && strpos($release['version'], '-') === false) { // Exclude all versions not compatible with the current version $versionParser = new VersionParser(); $version = $versionParser->getVersion($release['rawPlatformVersionSpec']); if ( // Major version is bigger or equals to the minimum version of the app version_compare($ncMajorVersion, $version->getMinimumVersion(), '>=') // Major version is smaller or equals to the maximum version of the app && version_compare($ncMajorVersion, $version->getMaximumVersion(), '<=') ) { $releases[] = $release; } } } // Get the highest version $versions = []; foreach($releases as $release) { $versions[] = $release['version']; } usort($versions, 'version_compare'); $versions = array_reverse($versions); $compatible = false; if(isset($versions[0])) { $highestVersion = $versions[0]; foreach ($releases as $release) { if ((string)$release['version'] === (string)$highestVersion) { $compatible = true; $response['data'][$dataKey]['releases'] = [$release]; break; } } } if(!$compatible) { unset($response['data'][$dataKey]); } } $response['data'] = array_values($response['data']); return $response; } private function setEndpoint() { $versionArray = explode('.', $this->getVersion()); $this->endpointUrl = sprintf( 'https://apps.nextcloud.com/api/v1/platform/%d.%d.%d/apps.json', $versionArray[0], $versionArray[1], $versionArray[2] ); } /** * @param string $version */ public function setVersion($version) { parent::setVersion($version); $this->setEndpoint(); } } private/App/DependencyAnalyzer.php 0000604 00000024566 15247130451 0013246 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App; use OCP\IL10N; class DependencyAnalyzer { /** @var Platform */ private $platform; /** @var \OCP\IL10N */ private $l; /** @var array */ private $appInfo; /** * @param Platform $platform * @param \OCP\IL10N $l */ function __construct(Platform $platform, IL10N $l) { $this->platform = $platform; $this->l = $l; } /** * @param array $app * @returns array of missing dependencies */ public function analyze(array $app) { $this->appInfo = $app; if (isset($app['dependencies'])) { $dependencies = $app['dependencies']; } else { $dependencies = []; } return array_merge( $this->analyzePhpVersion($dependencies), $this->analyzeDatabases($dependencies), $this->analyzeCommands($dependencies), $this->analyzeLibraries($dependencies), $this->analyzeOS($dependencies), $this->analyzeOC($dependencies, $app) ); } /** * Truncates both versions to the lowest common version, e.g. * 5.1.2.3 and 5.1 will be turned into 5.1 and 5.1, * 5.2.6.5 and 5.1 will be turned into 5.2 and 5.1 * @param string $first * @param string $second * @return string[] first element is the first version, second element is the * second version */ private function normalizeVersions($first, $second) { $first = explode('.', $first); $second = explode('.', $second); // get both arrays to the same minimum size $length = min(count($second), count($first)); $first = array_slice($first, 0, $length); $second = array_slice($second, 0, $length); return [implode('.', $first), implode('.', $second)]; } /** * Parameters will be normalized and then passed into version_compare * in the same order they are specified in the method header * @param string $first * @param string $second * @param string $operator * @return bool result similar to version_compare */ private function compare($first, $second, $operator) { // we can't normalize versions if one of the given parameters is not a // version string but null. In case one parameter is null normalization // will therefore be skipped if ($first !== null && $second !== null) { list($first, $second) = $this->normalizeVersions($first, $second); } return version_compare($first, $second, $operator); } /** * Checks if a version is bigger than another version * @param string $first * @param string $second * @return bool true if the first version is bigger than the second */ private function compareBigger($first, $second) { return $this->compare($first, $second, '>'); } /** * Checks if a version is smaller than another version * @param string $first * @param string $second * @return bool true if the first version is smaller than the second */ private function compareSmaller($first, $second) { return $this->compare($first, $second, '<'); } /** * @param array $dependencies * @return array */ private function analyzePhpVersion(array $dependencies) { $missing = []; if (isset($dependencies['php']['@attributes']['min-version'])) { $minVersion = $dependencies['php']['@attributes']['min-version']; if ($this->compareSmaller($this->platform->getPhpVersion(), $minVersion)) { $missing[] = (string)$this->l->t('PHP %s or higher is required.', $minVersion); } } if (isset($dependencies['php']['@attributes']['max-version'])) { $maxVersion = $dependencies['php']['@attributes']['max-version']; if ($this->compareBigger($this->platform->getPhpVersion(), $maxVersion)) { $missing[] = (string)$this->l->t('PHP with a version lower than %s is required.', $maxVersion); } } if (isset($dependencies['php']['@attributes']['min-int-size'])) { $intSize = $dependencies['php']['@attributes']['min-int-size']; if ($intSize > $this->platform->getIntSize()*8) { $missing[] = (string)$this->l->t('%sbit or higher PHP required.', $intSize); } } return $missing; } /** * @param array $dependencies * @return array */ private function analyzeDatabases(array $dependencies) { $missing = []; if (!isset($dependencies['database'])) { return $missing; } $supportedDatabases = $dependencies['database']; if (empty($supportedDatabases)) { return $missing; } if (!is_array($supportedDatabases)) { $supportedDatabases = array($supportedDatabases); } $supportedDatabases = array_map(function ($db) { return $this->getValue($db); }, $supportedDatabases); $currentDatabase = $this->platform->getDatabase(); if (!in_array($currentDatabase, $supportedDatabases)) { $missing[] = (string)$this->l->t('Following databases are supported: %s', join(', ', $supportedDatabases)); } return $missing; } /** * @param array $dependencies * @return array */ private function analyzeCommands(array $dependencies) { $missing = []; if (!isset($dependencies['command'])) { return $missing; } $commands = $dependencies['command']; if (!is_array($commands)) { $commands = array($commands); } if (isset($commands['@value'])) { $commands = [$commands]; } $os = $this->platform->getOS(); foreach ($commands as $command) { if (isset($command['@attributes']['os']) && $command['@attributes']['os'] !== $os) { continue; } $commandName = $this->getValue($command); if (!$this->platform->isCommandKnown($commandName)) { $missing[] = (string)$this->l->t('The command line tool %s could not be found', $commandName); } } return $missing; } /** * @param array $dependencies * @return array */ private function analyzeLibraries(array $dependencies) { $missing = []; if (!isset($dependencies['lib'])) { return $missing; } $libs = $dependencies['lib']; if (!is_array($libs)) { $libs = array($libs); } if (isset($libs['@value'])) { $libs = [$libs]; } foreach ($libs as $lib) { $libName = $this->getValue($lib); $libVersion = $this->platform->getLibraryVersion($libName); if (is_null($libVersion)) { $missing[] = (string)$this->l->t('The library %s is not available.', $libName); continue; } if (is_array($lib)) { if (isset($lib['@attributes']['min-version'])) { $minVersion = $lib['@attributes']['min-version']; if ($this->compareSmaller($libVersion, $minVersion)) { $missing[] = (string)$this->l->t('Library %s with a version higher than %s is required - available version %s.', array($libName, $minVersion, $libVersion)); } } if (isset($lib['@attributes']['max-version'])) { $maxVersion = $lib['@attributes']['max-version']; if ($this->compareBigger($libVersion, $maxVersion)) { $missing[] = (string)$this->l->t('Library %s with a version lower than %s is required - available version %s.', array($libName, $maxVersion, $libVersion)); } } } } return $missing; } /** * @param array $dependencies * @return array */ private function analyzeOS(array $dependencies) { $missing = []; if (!isset($dependencies['os'])) { return $missing; } $oss = $dependencies['os']; if (empty($oss)) { return $missing; } if (is_array($oss)) { $oss = array_map(function ($os) { return $this->getValue($os); }, $oss); } else { $oss = array($oss); } $currentOS = $this->platform->getOS(); if (!in_array($currentOS, $oss)) { $missing[] = (string)$this->l->t('Following platforms are supported: %s', join(', ', $oss)); } return $missing; } /** * @param array $dependencies * @param array $appInfo * @return array */ private function analyzeOC(array $dependencies, array $appInfo) { $missing = []; $minVersion = null; if (isset($dependencies['nextcloud']['@attributes']['min-version'])) { $minVersion = $dependencies['nextcloud']['@attributes']['min-version']; } elseif (isset($dependencies['owncloud']['@attributes']['min-version'])) { $minVersion = $dependencies['owncloud']['@attributes']['min-version']; } elseif (isset($appInfo['requiremin'])) { $minVersion = $appInfo['requiremin']; } elseif (isset($appInfo['require'])) { $minVersion = $appInfo['require']; } $maxVersion = null; if (isset($dependencies['nextcloud']['@attributes']['max-version'])) { $maxVersion = $dependencies['nextcloud']['@attributes']['max-version']; } elseif (isset($dependencies['owncloud']['@attributes']['max-version'])) { $maxVersion = $dependencies['owncloud']['@attributes']['max-version']; } elseif (isset($appInfo['requiremax'])) { $maxVersion = $appInfo['requiremax']; } if (!is_null($minVersion)) { if ($this->compareSmaller($this->platform->getOcVersion(), $minVersion)) { $missing[] = (string)$this->l->t('Server version %s or higher is required.', $this->toVisibleVersion($minVersion)); } } if (!is_null($maxVersion)) { if ($this->compareBigger($this->platform->getOcVersion(), $maxVersion)) { $missing[] = (string)$this->l->t('Server version %s or lower is required.', $this->toVisibleVersion($maxVersion)); } } return $missing; } /** * Map the internal version number to the Nextcloud version * * @param string $version * @return string */ protected function toVisibleVersion($version) { switch ($version) { case '9.1': return '10'; default: if (strpos($version, '9.1.') === 0) { $version = '10.0.' . substr($version, 4); } return $version; } } /** * @param $element * @return mixed */ private function getValue($element) { if (isset($element['@value'])) return $element['@value']; return (string)$element; } } private/App/Platform.php 0000604 00000004037 15247130451 0011235 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\App; use OCP\IConfig; /** * Class Platform * * This class basically abstracts any kind of information which can be retrieved from the underlying system. * * @package OC\App */ class Platform { /** * @param IConfig $config */ function __construct(IConfig $config) { $this->config = $config; } /** * @return string */ public function getPhpVersion() { return phpversion(); } /** * @return int */ public function getIntSize() { return PHP_INT_SIZE; } /** * @return string */ public function getOcVersion() { $v = \OCP\Util::getVersion(); return join('.', $v); } /** * @return string */ public function getDatabase() { $dbType = $this->config->getSystemValue('dbtype', 'sqlite'); if ($dbType === 'sqlite3') { $dbType = 'sqlite'; } return $dbType; } /** * @return string */ public function getOS() { return php_uname('s'); } /** * @param $command * @return bool */ public function isCommandKnown($command) { $path = \OC_Helper::findBinaryPath($command); return ($path !== null); } public function getLibraryVersion($name) { $repo = new PlatformRepository(); $lib = $repo->findLibrary($name); return $lib; } } private/RepairException.php 0000604 00000001665 15247130451 0012036 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; /** * Exception thrown whenever a database/migration repair * could not be done. */ class RepairException extends \Exception { } private/Archive/TAR.php 0000604 00000021715 15247130451 0010742 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Christian Weiske <cweiske@cweiske.de> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Felix Moeller <mail@felixmoeller.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Remco Brenninkmeijer <requist1@starmail.nl> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Archive; use Icewind\Streams\CallbackWrapper; class TAR extends Archive { const PLAIN = 0; const GZIP = 1; const BZIP = 2; private $fileList; private $cachedHeaders; /** * @var \Archive_Tar tar */ private $tar = null; private $path; /** * @param string $source */ function __construct($source) { $types = array(null, 'gz', 'bz2'); $this->path = $source; $this->tar = new \Archive_Tar($source, $types[self::getTarType($source)]); } /** * try to detect the type of tar compression * * @param string $file * @return integer */ static public function getTarType($file) { if (strpos($file, '.')) { $extension = substr($file, strrpos($file, '.')); switch ($extension) { case '.gz': case '.tgz': return self::GZIP; case '.bz': case '.bz2': return self::BZIP; case '.tar': return self::PLAIN; default: return self::PLAIN; } } else { return self::PLAIN; } } /** * add an empty folder to the archive * * @param string $path * @return bool */ function addFolder($path) { $tmpBase = \OC::$server->getTempManager()->getTemporaryFolder(); if (substr($path, -1, 1) != '/') { $path .= '/'; } if ($this->fileExists($path)) { return false; } $parts = explode('/', $path); $folder = $tmpBase; foreach ($parts as $part) { $folder .= '/' . $part; if (!is_dir($folder)) { mkdir($folder); } } $result = $this->tar->addModify(array($tmpBase . $path), '', $tmpBase); rmdir($tmpBase . $path); $this->fileList = false; $this->cachedHeaders = false; return $result; } /** * add a file to the archive * * @param string $path * @param string $source either a local file or string data * @return bool */ function addFile($path, $source = '') { if ($this->fileExists($path)) { $this->remove($path); } if ($source and $source[0] == '/' and file_exists($source)) { $source = file_get_contents($source); } $result = $this->tar->addString($path, $source); $this->fileList = false; $this->cachedHeaders = false; return $result; } /** * rename a file or folder in the archive * * @param string $source * @param string $dest * @return bool */ function rename($source, $dest) { //no proper way to delete, rename entire archive, rename file and remake archive $tmp = \OCP\Files::tmpFolder(); $this->tar->extract($tmp); rename($tmp . $source, $tmp . $dest); $this->tar = null; unlink($this->path); $types = array(null, 'gz', 'bz'); $this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]); $this->tar->createModify(array($tmp), '', $tmp . '/'); $this->fileList = false; $this->cachedHeaders = false; return true; } /** * @param string $file */ private function getHeader($file) { if (!$this->cachedHeaders) { $this->cachedHeaders = $this->tar->listContent(); } foreach ($this->cachedHeaders as $header) { if ($file == $header['filename'] or $file . '/' == $header['filename'] or '/' . $file . '/' == $header['filename'] or '/' . $file == $header['filename'] ) { return $header; } } return null; } /** * get the uncompressed size of a file in the archive * * @param string $path * @return int */ function filesize($path) { $stat = $this->getHeader($path); return $stat['size']; } /** * get the last modified time of a file in the archive * * @param string $path * @return int */ function mtime($path) { $stat = $this->getHeader($path); return $stat['mtime']; } /** * get the files in a folder * * @param string $path * @return array */ function getFolder($path) { $files = $this->getFiles(); $folderContent = array(); $pathLength = strlen($path); foreach ($files as $file) { if ($file[0] == '/') { $file = substr($file, 1); } if (substr($file, 0, $pathLength) == $path and $file != $path) { $result = substr($file, $pathLength); if ($pos = strpos($result, '/')) { $result = substr($result, 0, $pos + 1); } if (array_search($result, $folderContent) === false) { $folderContent[] = $result; } } } return $folderContent; } /** * get all files in the archive * * @return array */ function getFiles() { if ($this->fileList) { return $this->fileList; } if (!$this->cachedHeaders) { $this->cachedHeaders = $this->tar->listContent(); } $files = array(); foreach ($this->cachedHeaders as $header) { $files[] = $header['filename']; } $this->fileList = $files; return $files; } /** * get the content of a file * * @param string $path * @return string */ function getFile($path) { return $this->tar->extractInString($path); } /** * extract a single file from the archive * * @param string $path * @param string $dest * @return bool */ function extractFile($path, $dest) { $tmp = \OCP\Files::tmpFolder(); if (!$this->fileExists($path)) { return false; } if ($this->fileExists('/' . $path)) { $success = $this->tar->extractList(array('/' . $path), $tmp); } else { $success = $this->tar->extractList(array($path), $tmp); } if ($success) { rename($tmp . $path, $dest); } \OCP\Files::rmdirr($tmp); return $success; } /** * extract the archive * * @param string $dest * @return bool */ function extract($dest) { return $this->tar->extract($dest); } /** * check if a file or folder exists in the archive * * @param string $path * @return bool */ function fileExists($path) { $files = $this->getFiles(); if ((array_search($path, $files) !== false) or (array_search($path . '/', $files) !== false)) { return true; } else { $folderPath = $path; if (substr($folderPath, -1, 1) != '/') { $folderPath .= '/'; } $pathLength = strlen($folderPath); foreach ($files as $file) { if (strlen($file) > $pathLength and substr($file, 0, $pathLength) == $folderPath) { return true; } } } if ($path[0] != '/') { //not all programs agree on the use of a leading / return $this->fileExists('/' . $path); } else { return false; } } /** * remove a file or folder from the archive * * @param string $path * @return bool */ function remove($path) { if (!$this->fileExists($path)) { return false; } $this->fileList = false; $this->cachedHeaders = false; //no proper way to delete, extract entire archive, delete file and remake archive $tmp = \OCP\Files::tmpFolder(); $this->tar->extract($tmp); \OCP\Files::rmdirr($tmp . $path); $this->tar = null; unlink($this->path); $this->reopen(); $this->tar->createModify(array($tmp), '', $tmp); return true; } /** * get a file handler * * @param string $path * @param string $mode * @return resource */ function getStream($path, $mode) { if (strrpos($path, '.') !== false) { $ext = substr($path, strrpos($path, '.')); } else { $ext = ''; } $tmpFile = \OCP\Files::tmpFile($ext); if ($this->fileExists($path)) { $this->extractFile($path, $tmpFile); } elseif ($mode == 'r' or $mode == 'rb') { return false; } if ($mode == 'r' or $mode == 'rb') { return fopen($tmpFile, $mode); } else { $handle = fopen($tmpFile, $mode); return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { $this->writeBack($tmpFile, $path); }); } } /** * write back temporary files */ function writeBack($tmpFile, $path) { $this->addFile($path, $tmpFile); unlink($tmpFile); } /** * reopen the archive to ensure everything is written */ private function reopen() { if ($this->tar) { $this->tar->_close(); $this->tar = null; } $types = array(null, 'gz', 'bz'); $this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]); } } private/Archive/Archive.php 0000604 00000007131 15247130451 0011671 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Felix Moeller <mail@felixmoeller.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Archive; abstract class Archive { /** * @param $source */ abstract function __construct($source); /** * add an empty folder to the archive * @param string $path * @return bool */ abstract function addFolder($path); /** * add a file to the archive * @param string $path * @param string $source either a local file or string data * @return bool */ abstract function addFile($path, $source=''); /** * rename a file or folder in the archive * @param string $source * @param string $dest * @return bool */ abstract function rename($source, $dest); /** * get the uncompressed size of a file in the archive * @param string $path * @return int */ abstract function filesize($path); /** * get the last modified time of a file in the archive * @param string $path * @return int */ abstract function mtime($path); /** * get the files in a folder * @param string $path * @return array */ abstract function getFolder($path); /** * get all files in the archive * @return array */ abstract function getFiles(); /** * get the content of a file * @param string $path * @return string */ abstract function getFile($path); /** * extract a single file from the archive * @param string $path * @param string $dest * @return bool */ abstract function extractFile($path, $dest); /** * extract the archive * @param string $dest * @return bool */ abstract function extract($dest); /** * check if a file or folder exists in the archive * @param string $path * @return bool */ abstract function fileExists($path); /** * remove a file or folder from the archive * @param string $path * @return bool */ abstract function remove($path); /** * get a file handler * @param string $path * @param string $mode * @return resource */ abstract function getStream($path, $mode); /** * add a folder and all its content * @param string $path * @param string $source * @return boolean|null */ function addRecursive($path, $source) { $dh = opendir($source); if(is_resource($dh)) { $this->addFolder($path); while (($file = readdir($dh)) !== false) { if($file=='.' or $file=='..') { continue; } if(is_dir($source.'/'.$file)) { $this->addRecursive($path.'/'.$file, $source.'/'.$file); }else{ $this->addFile($path.'/'.$file, $source.'/'.$file); } } } } } private/Archive/ZIP.php 0000604 00000013154 15247130451 0010754 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Felix Moeller <mail@felixmoeller.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Archive; use Icewind\Streams\CallbackWrapper; class ZIP extends Archive{ /** * @var \ZipArchive zip */ private $zip=null; private $path; /** * @param string $source */ function __construct($source) { $this->path=$source; $this->zip=new \ZipArchive(); if($this->zip->open($source, \ZipArchive::CREATE)) { }else{ \OCP\Util::writeLog('files_archive', 'Error while opening archive '.$source, \OCP\Util::WARN); } } /** * add an empty folder to the archive * @param string $path * @return bool */ function addFolder($path) { return $this->zip->addEmptyDir($path); } /** * add a file to the archive * @param string $path * @param string $source either a local file or string data * @return bool */ function addFile($path, $source='') { if($source and $source[0]=='/' and file_exists($source)) { $result=$this->zip->addFile($source, $path); }else{ $result=$this->zip->addFromString($path, $source); } if($result) { $this->zip->close();//close and reopen to save the zip $this->zip->open($this->path); } return $result; } /** * rename a file or folder in the archive * @param string $source * @param string $dest * @return boolean|null */ function rename($source, $dest) { $source=$this->stripPath($source); $dest=$this->stripPath($dest); $this->zip->renameName($source, $dest); } /** * get the uncompressed size of a file in the archive * @param string $path * @return int */ function filesize($path) { $stat=$this->zip->statName($path); return $stat['size']; } /** * get the last modified time of a file in the archive * @param string $path * @return int */ function mtime($path) { return filemtime($this->path); } /** * get the files in a folder * @param string $path * @return array */ function getFolder($path) { $files=$this->getFiles(); $folderContent=array(); $pathLength=strlen($path); foreach($files as $file) { if(substr($file, 0, $pathLength)==$path and $file!=$path) { if(strrpos(substr($file, 0, -1), '/')<=$pathLength) { $folderContent[]=substr($file, $pathLength); } } } return $folderContent; } /** * get all files in the archive * @return array */ function getFiles() { $fileCount=$this->zip->numFiles; $files=array(); for($i=0;$i<$fileCount;$i++) { $files[]=$this->zip->getNameIndex($i); } return $files; } /** * get the content of a file * @param string $path * @return string */ function getFile($path) { return $this->zip->getFromName($path); } /** * extract a single file from the archive * @param string $path * @param string $dest * @return boolean|null */ function extractFile($path, $dest) { $fp = $this->zip->getStream($path); file_put_contents($dest, $fp); } /** * extract the archive * @param string $dest * @return bool */ function extract($dest) { return $this->zip->extractTo($dest); } /** * check if a file or folder exists in the archive * @param string $path * @return bool */ function fileExists($path) { return ($this->zip->locateName($path)!==false) or ($this->zip->locateName($path.'/')!==false); } /** * remove a file or folder from the archive * @param string $path * @return bool */ function remove($path) { if($this->fileExists($path.'/')) { return $this->zip->deleteName($path.'/'); }else{ return $this->zip->deleteName($path); } } /** * get a file handler * @param string $path * @param string $mode * @return resource */ function getStream($path, $mode) { if($mode=='r' or $mode=='rb') { return $this->zip->getStream($path); } else { //since we can't directly get a writable stream, //make a temp copy of the file and put it back //in the archive when the stream is closed if(strrpos($path, '.')!==false) { $ext=substr($path, strrpos($path, '.')); }else{ $ext=''; } $tmpFile=\OCP\Files::tmpFile($ext); if($this->fileExists($path)) { $this->extractFile($path, $tmpFile); } $handle = fopen($tmpFile, $mode); return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { $this->writeBack($tmpFile, $path); }); } } /** * write back temporary files */ function writeBack($tmpFile, $path) { $this->addFile($path, $tmpFile); unlink($tmpFile); } /** * @param string $path * @return string */ private function stripPath($path) { if(!$path || $path[0]=='/') { return substr($path, 1); }else{ return $path; } } } private/HintException.php 0000604 00000004535 15247130451 0011515 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; /** * Class HintException * * An Exception class with the intention to be presented to the end user * * @package OC */ class HintException extends \Exception { private $hint; /** * HintException constructor. * * @param string $message The error message. It will be not revealed to the * the user (unless the hint is empty) and thus * should be not translated. * @param string $hint A useful message that is presented to the end * user. It should be translated, but must not * contain sensitive data. * @param int $code * @param \Exception|null $previous */ public function __construct($message, $hint = '', $code = 0, \Exception $previous = null) { $this->hint = $hint; parent::__construct($message, $code, $previous); } /** * Returns a string representation of this Exception that includes the error * code, the message and the hint. * * @return string */ public function __toString() { return __CLASS__ . ": [{$this->code}]: {$this->message} ({$this->hint})\n"; } /** * Returns the hint with the intention to be presented to the end user. If * an empty hint was specified upon instatiation, the message is returned * instead. * * @return string */ public function getHint() { if (empty($this->hint)) { return $this->message; } return $this->hint; } } private/Accounts/AccountManager.php 0000604 00000023756 15247130451 0013410 0 ustar 00 <?php /** * @author Björn Schießle <bjoern@schiessle.org> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Björn Schießle * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Accounts; use OCP\BackgroundJob\IJobList; use OCP\IDBConnection; use OCP\IUser; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; /** * Class AccountManager * * Manage system accounts table * * @group DB * @package OC\Accounts */ class AccountManager { /** nobody can see my account details */ const VISIBILITY_PRIVATE = 'private'; /** only contacts, especially trusted servers can see my contact details */ const VISIBILITY_CONTACTS_ONLY = 'contacts'; /** every body ca see my contact detail, will be published to the lookup server */ const VISIBILITY_PUBLIC = 'public'; const PROPERTY_AVATAR = 'avatar'; const PROPERTY_DISPLAYNAME = 'displayname'; const PROPERTY_PHONE = 'phone'; const PROPERTY_EMAIL = 'email'; const PROPERTY_WEBSITE = 'website'; const PROPERTY_ADDRESS = 'address'; const PROPERTY_TWITTER = 'twitter'; const NOT_VERIFIED = '0'; const VERIFICATION_IN_PROGRESS = '1'; const VERIFIED = '2'; /** @var IDBConnection database connection */ private $connection; /** @var string table name */ private $table = 'accounts'; /** @var EventDispatcherInterface */ private $eventDispatcher; /** @var IJobList */ private $jobList; /** * AccountManager constructor. * * @param IDBConnection $connection * @param EventDispatcherInterface $eventDispatcher * @param IJobList $jobList */ public function __construct(IDBConnection $connection, EventDispatcherInterface $eventDispatcher, IJobList $jobList) { $this->connection = $connection; $this->eventDispatcher = $eventDispatcher; $this->jobList = $jobList; } /** * update user record * * @param IUser $user * @param $data */ public function updateUser(IUser $user, $data) { $userData = $this->getUser($user); $updated = true; if (empty($userData)) { $this->insertNewUser($user, $data); } elseif ($userData !== $data) { $data = $this->checkEmailVerification($userData, $data, $user); $data = $this->updateVerifyStatus($userData, $data); $this->updateExistingUser($user, $data); } else { // nothing needs to be done if new and old data set are the same $updated = false; } if ($updated) { $this->eventDispatcher->dispatch( 'OC\AccountManager::userUpdated', new GenericEvent($user, $data) ); } } /** * delete user from accounts table * * @param IUser $user */ public function deleteUser(IUser $user) { $uid = $user->getUID(); $query = $this->connection->getQueryBuilder(); $query->delete($this->table) ->where($query->expr()->eq('uid', $query->createNamedParameter($uid))) ->execute(); } /** * get stored data from a given user * * @param IUser $user * @return array */ public function getUser(IUser $user) { $uid = $user->getUID(); $query = $this->connection->getQueryBuilder(); $query->select('data')->from($this->table) ->where($query->expr()->eq('uid', $query->createParameter('uid'))) ->setParameter('uid', $uid); $query->execute(); $result = $query->execute()->fetchAll(); if (empty($result)) { $userData = $this->buildDefaultUserRecord($user); $this->insertNewUser($user, $userData); return $userData; } $userDataArray = json_decode($result[0]['data'], true); $userDataArray = $this->addMissingDefaultValues($userDataArray); return $userDataArray; } /** * check if we need to ask the server for email verification, if yes we create a cronjob * * @param $oldData * @param $newData * @param IUser $user * @return array */ protected function checkEmailVerification($oldData, $newData, IUser $user) { if ($oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']) { $this->jobList->add('OC\Settings\BackgroundJobs\VerifyUserData', [ 'verificationCode' => '', 'data' => $newData[self::PROPERTY_EMAIL]['value'], 'type' => self::PROPERTY_EMAIL, 'uid' => $user->getUID(), 'try' => 0, 'lastRun' => time() ] ); $newData[AccountManager::PROPERTY_EMAIL]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS; } return $newData; } /** * make sure that all expected data are set * * @param array $userData * @return array */ protected function addMissingDefaultValues(array $userData) { foreach ($userData as $key => $value) { if (!isset($userData[$key]['verified'])) { $userData[$key]['verified'] = self::NOT_VERIFIED; } } return $userData; } /** * reset verification status if personal data changed * * @param array $oldData * @param array $newData * @return array */ protected function updateVerifyStatus($oldData, $newData) { // which account was already verified successfully? $twitterVerified = isset($oldData[self::PROPERTY_TWITTER]['verified']) && $oldData[self::PROPERTY_TWITTER]['verified'] === self::VERIFIED; $websiteVerified = isset($oldData[self::PROPERTY_WEBSITE]['verified']) && $oldData[self::PROPERTY_WEBSITE]['verified'] === self::VERIFIED; $emailVerified = isset($oldData[self::PROPERTY_EMAIL]['verified']) && $oldData[self::PROPERTY_EMAIL]['verified'] === self::VERIFIED; // keep old verification status if we don't have a new one if(!isset($newData[self::PROPERTY_TWITTER]['verified'])) { // keep old verification status if value didn't changed and an old value exists $keepOldStatus = $newData[self::PROPERTY_TWITTER]['value'] === $oldData[self::PROPERTY_TWITTER]['value'] && isset($oldData[self::PROPERTY_TWITTER]['verified']); $newData[self::PROPERTY_TWITTER]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_TWITTER]['verified'] : self::NOT_VERIFIED; } if(!isset($newData[self::PROPERTY_WEBSITE]['verified'])) { // keep old verification status if value didn't changed and an old value exists $keepOldStatus = $newData[self::PROPERTY_WEBSITE]['value'] === $oldData[self::PROPERTY_WEBSITE]['value'] && isset($oldData[self::PROPERTY_WEBSITE]['verified']); $newData[self::PROPERTY_WEBSITE]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_WEBSITE]['verified'] : self::NOT_VERIFIED; } if(!isset($newData[self::PROPERTY_EMAIL]['verified'])) { // keep old verification status if value didn't changed and an old value exists $keepOldStatus = $newData[self::PROPERTY_EMAIL]['value'] === $oldData[self::PROPERTY_EMAIL]['value'] && isset($oldData[self::PROPERTY_EMAIL]['verified']); $newData[self::PROPERTY_EMAIL]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_EMAIL]['verified'] : self::VERIFICATION_IN_PROGRESS; } // reset verification status if a value from a previously verified data was changed if($twitterVerified && $oldData[self::PROPERTY_TWITTER]['value'] !== $newData[self::PROPERTY_TWITTER]['value'] ) { $newData[self::PROPERTY_TWITTER]['verified'] = self::NOT_VERIFIED; } if($websiteVerified && $oldData[self::PROPERTY_WEBSITE]['value'] !== $newData[self::PROPERTY_WEBSITE]['value'] ) { $newData[self::PROPERTY_WEBSITE]['verified'] = self::NOT_VERIFIED; } if($emailVerified && $oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value'] ) { $newData[self::PROPERTY_EMAIL]['verified'] = self::NOT_VERIFIED; } return $newData; } /** * add new user to accounts table * * @param IUser $user * @param array $data */ protected function insertNewUser(IUser $user, $data) { $uid = $user->getUID(); $jsonEncodedData = json_encode($data); $query = $this->connection->getQueryBuilder(); $query->insert($this->table) ->values( [ 'uid' => $query->createNamedParameter($uid), 'data' => $query->createNamedParameter($jsonEncodedData), ] ) ->execute(); } /** * update existing user in accounts table * * @param IUser $user * @param array $data */ protected function updateExistingUser(IUser $user, $data) { $uid = $user->getUID(); $jsonEncodedData = json_encode($data); $query = $this->connection->getQueryBuilder(); $query->update($this->table) ->set('data', $query->createNamedParameter($jsonEncodedData)) ->where($query->expr()->eq('uid', $query->createNamedParameter($uid))) ->execute(); } /** * build default user record in case not data set exists yet * * @param IUser $user * @return array */ protected function buildDefaultUserRecord(IUser $user) { return [ self::PROPERTY_DISPLAYNAME => [ 'value' => $user->getDisplayName(), 'scope' => self::VISIBILITY_CONTACTS_ONLY, 'verified' => self::NOT_VERIFIED, ], self::PROPERTY_ADDRESS => [ 'value' => '', 'scope' => self::VISIBILITY_PRIVATE, 'verified' => self::NOT_VERIFIED, ], self::PROPERTY_WEBSITE => [ 'value' => '', 'scope' => self::VISIBILITY_PRIVATE, 'verified' => self::NOT_VERIFIED, ], self::PROPERTY_EMAIL => [ 'value' => $user->getEMailAddress(), 'scope' => self::VISIBILITY_CONTACTS_ONLY, 'verified' => self::NOT_VERIFIED, ], self::PROPERTY_AVATAR => [ 'scope' => self::VISIBILITY_CONTACTS_ONLY ], self::PROPERTY_PHONE => [ 'value' => '', 'scope' => self::VISIBILITY_PRIVATE, 'verified' => self::NOT_VERIFIED, ], self::PROPERTY_TWITTER => [ 'value' => '', 'scope' => self::VISIBILITY_PRIVATE, 'verified' => self::NOT_VERIFIED, ], ]; } } private/Accounts/Hooks.php 0000604 00000005200 15247130451 0011564 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Accounts; use OCP\ILogger; use OCP\IUser; class Hooks { /** @var AccountManager */ private $accountManager = null; /** @var ILogger */ private $logger; /** * Hooks constructor. * * @param ILogger $logger */ public function __construct(ILogger $logger) { $this->logger = $logger; } /** * update accounts table if email address or display name was changed from outside * * @param array $params */ public function changeUserHook($params) { $accountManager = $this->getAccountManager(); /** @var IUser $user */ $user = isset($params['user']) ? $params['user'] : null; $feature = isset($params['feature']) ? $params['feature'] : null; $newValue = isset($params['value']) ? $params['value'] : null; if (is_null($user) || is_null($feature) || is_null($newValue)) { $this->logger->warning('Missing expected parameters in change user hook'); return; } $accountData = $accountManager->getUser($user); switch ($feature) { case 'eMailAddress': if ($accountData[AccountManager::PROPERTY_EMAIL]['value'] !== $newValue) { $accountData[AccountManager::PROPERTY_EMAIL]['value'] = $newValue; $accountManager->updateUser($user, $accountData); } break; case 'displayName': if ($accountData[AccountManager::PROPERTY_DISPLAYNAME]['value'] !== $newValue) { $accountData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $newValue; $accountManager->updateUser($user, $accountData); } break; } } /** * return instance of accountManager * * @return AccountManager */ protected function getAccountManager() { if (is_null($this->accountManager)) { $this->accountManager = new AccountManager( \OC::$server->getDatabaseConnection(), \OC::$server->getEventDispatcher(), \OC::$server->getJobList() ); } return $this->accountManager; } } private/SystemTag/SystemTag.php 0000604 00000003513 15247130451 0012567 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\SystemTag; use OCP\SystemTag\ISystemTag; class SystemTag implements ISystemTag { /** * @var string */ private $id; /** * @var string */ private $name; /** * @var bool */ private $userVisible; /** * @var bool */ private $userAssignable; /** * Constructor. * * @param string $id tag id * @param string $name tag name * @param bool $userVisible whether the tag is user visible * @param bool $userAssignable whether the tag is user assignable */ public function __construct($id, $name, $userVisible, $userAssignable) { $this->id = $id; $this->name = $name; $this->userVisible = $userVisible; $this->userAssignable = $userAssignable; } /** * {@inheritdoc} */ public function getId() { return $this->id; } /** * {@inheritdoc} */ public function getName() { return $this->name; } /** * {@inheritdoc} */ public function isUserVisible() { return $this->userVisible; } /** * {@inheritdoc} */ public function isUserAssignable() { return $this->userAssignable; } } private/SystemTag/ManagerFactory.php 0000604 00000004156 15247130451 0013555 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\SystemTag; use OCP\IServerContainer; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagManagerFactory; use OCP\SystemTag\ISystemTagObjectMapper; /** * Default factory class for system tag managers * * @package OCP\SystemTag * @since 9.0.0 */ class ManagerFactory implements ISystemTagManagerFactory { /** * Server container * * @var IServerContainer */ private $serverContainer; /** * Constructor for the system tag manager factory * * @param IServerContainer $serverContainer server container */ public function __construct(IServerContainer $serverContainer) { $this->serverContainer = $serverContainer; } /** * Creates and returns an instance of the system tag manager * * @return ISystemTagManager * @since 9.0.0 */ public function getManager() { return new SystemTagManager( $this->serverContainer->getDatabaseConnection(), $this->serverContainer->getGroupManager(), $this->serverContainer->getEventDispatcher() ); } /** * Creates and returns an instance of the system tag object * mapper * * @return ISystemTagObjectMapper * @since 9.0.0 */ public function getObjectMapper() { return new SystemTagObjectMapper( $this->serverContainer->getDatabaseConnection(), $this->getManager(), $this->serverContainer->getEventDispatcher() ); } } private/SystemTag/SystemTagManager.php 0000604 00000026103 15247130451 0014062 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\SystemTag; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ManagerEvent; use OCP\SystemTag\TagAlreadyExistsException; use OCP\SystemTag\TagNotFoundException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use OCP\IGroupManager; use OCP\SystemTag\ISystemTag; use OCP\IUser; /** * Manager class for system tags */ class SystemTagManager implements ISystemTagManager { const TAG_TABLE = 'systemtag'; const TAG_GROUP_TABLE = 'systemtag_group'; /** @var IDBConnection */ protected $connection; /** @var EventDispatcherInterface */ protected $dispatcher; /** @var IGroupManager */ protected $groupManager; /** * Prepared query for selecting tags directly * * @var \OCP\DB\QueryBuilder\IQueryBuilder */ private $selectTagQuery; /** * Constructor. * * @param IDBConnection $connection database connection * @param EventDispatcherInterface $dispatcher */ public function __construct( IDBConnection $connection, IGroupManager $groupManager, EventDispatcherInterface $dispatcher ) { $this->connection = $connection; $this->groupManager = $groupManager; $this->dispatcher = $dispatcher; $query = $this->connection->getQueryBuilder(); $this->selectTagQuery = $query->select('*') ->from(self::TAG_TABLE) ->where($query->expr()->eq('name', $query->createParameter('name'))) ->andWhere($query->expr()->eq('visibility', $query->createParameter('visibility'))) ->andWhere($query->expr()->eq('editable', $query->createParameter('editable'))); } /** * {@inheritdoc} */ public function getTagsByIds($tagIds) { if (!is_array($tagIds)) { $tagIds = [$tagIds]; } $tags = []; // note: not all databases will fail if it's a string or starts with a number foreach ($tagIds as $tagId) { if (!is_numeric($tagId)) { throw new \InvalidArgumentException('Tag id must be integer'); } } $query = $this->connection->getQueryBuilder(); $query->select('*') ->from(self::TAG_TABLE) ->where($query->expr()->in('id', $query->createParameter('tagids'))) ->addOrderBy('name', 'ASC') ->addOrderBy('visibility', 'ASC') ->addOrderBy('editable', 'ASC') ->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY); $result = $query->execute(); while ($row = $result->fetch()) { $tags[$row['id']] = $this->createSystemTagFromRow($row); } $result->closeCursor(); if (count($tags) !== count($tagIds)) { throw new TagNotFoundException( 'Tag id(s) not found', 0, null, array_diff($tagIds, array_keys($tags)) ); } return $tags; } /** * {@inheritdoc} */ public function getAllTags($visibilityFilter = null, $nameSearchPattern = null) { $tags = []; $query = $this->connection->getQueryBuilder(); $query->select('*') ->from(self::TAG_TABLE); if (!is_null($visibilityFilter)) { $query->andWhere($query->expr()->eq('visibility', $query->createNamedParameter((int)$visibilityFilter))); } if (!empty($nameSearchPattern)) { $query->andWhere( $query->expr()->like( 'name', $query->createNamedParameter('%' . $this->connection->escapeLikeParameter($nameSearchPattern). '%') ) ); } $query ->addOrderBy('name', 'ASC') ->addOrderBy('visibility', 'ASC') ->addOrderBy('editable', 'ASC'); $result = $query->execute(); while ($row = $result->fetch()) { $tags[$row['id']] = $this->createSystemTagFromRow($row); } $result->closeCursor(); return $tags; } /** * {@inheritdoc} */ public function getTag($tagName, $userVisible, $userAssignable) { $userVisible = (int)$userVisible; $userAssignable = (int)$userAssignable; $result = $this->selectTagQuery ->setParameter('name', $tagName) ->setParameter('visibility', $userVisible) ->setParameter('editable', $userAssignable) ->execute(); $row = $result->fetch(); $result->closeCursor(); if (!$row) { throw new TagNotFoundException( 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') does not exist' ); } return $this->createSystemTagFromRow($row); } /** * {@inheritdoc} */ public function createTag($tagName, $userVisible, $userAssignable) { $userVisible = (int)$userVisible; $userAssignable = (int)$userAssignable; $query = $this->connection->getQueryBuilder(); $query->insert(self::TAG_TABLE) ->values([ 'name' => $query->createNamedParameter($tagName), 'visibility' => $query->createNamedParameter($userVisible), 'editable' => $query->createNamedParameter($userAssignable), ]); try { $query->execute(); } catch (UniqueConstraintViolationException $e) { throw new TagAlreadyExistsException( 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') already exists', 0, $e ); } $tagId = $query->getLastInsertId(); $tag = new SystemTag( (int)$tagId, $tagName, (bool)$userVisible, (bool)$userAssignable ); $this->dispatcher->dispatch(ManagerEvent::EVENT_CREATE, new ManagerEvent( ManagerEvent::EVENT_CREATE, $tag )); return $tag; } /** * {@inheritdoc} */ public function updateTag($tagId, $tagName, $userVisible, $userAssignable) { $userVisible = (int)$userVisible; $userAssignable = (int)$userAssignable; try { $tags = $this->getTagsByIds($tagId); } catch (TagNotFoundException $e) { throw new TagNotFoundException( 'Tag does not exist', 0, null, [$tagId] ); } $beforeUpdate = array_shift($tags); $afterUpdate = new SystemTag( (int) $tagId, $tagName, (bool) $userVisible, (bool) $userAssignable ); $query = $this->connection->getQueryBuilder(); $query->update(self::TAG_TABLE) ->set('name', $query->createParameter('name')) ->set('visibility', $query->createParameter('visibility')) ->set('editable', $query->createParameter('editable')) ->where($query->expr()->eq('id', $query->createParameter('tagid'))) ->setParameter('name', $tagName) ->setParameter('visibility', $userVisible) ->setParameter('editable', $userAssignable) ->setParameter('tagid', $tagId); try { if ($query->execute() === 0) { throw new TagNotFoundException( 'Tag does not exist', 0, null, [$tagId] ); } } catch (UniqueConstraintViolationException $e) { throw new TagAlreadyExistsException( 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') already exists', 0, $e ); } $this->dispatcher->dispatch(ManagerEvent::EVENT_UPDATE, new ManagerEvent( ManagerEvent::EVENT_UPDATE, $afterUpdate, $beforeUpdate )); } /** * {@inheritdoc} */ public function deleteTags($tagIds) { if (!is_array($tagIds)) { $tagIds = [$tagIds]; } $tagNotFoundException = null; $tags = []; try { $tags = $this->getTagsByIds($tagIds); } catch (TagNotFoundException $e) { $tagNotFoundException = $e; // Get existing tag objects for the hooks later $existingTags = array_diff($tagIds, $tagNotFoundException->getMissingTags()); if (!empty($existingTags)) { try { $tags = $this->getTagsByIds($existingTags); } catch (TagNotFoundException $e) { // Ignore further errors... } } } // delete relationships first $query = $this->connection->getQueryBuilder(); $query->delete(SystemTagObjectMapper::RELATION_TABLE) ->where($query->expr()->in('systemtagid', $query->createParameter('tagids'))) ->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY) ->execute(); $query = $this->connection->getQueryBuilder(); $query->delete(self::TAG_TABLE) ->where($query->expr()->in('id', $query->createParameter('tagids'))) ->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY) ->execute(); foreach ($tags as $tag) { $this->dispatcher->dispatch(ManagerEvent::EVENT_DELETE, new ManagerEvent( ManagerEvent::EVENT_DELETE, $tag )); } if ($tagNotFoundException !== null) { throw new TagNotFoundException( 'Tag id(s) not found', 0, $tagNotFoundException, $tagNotFoundException->getMissingTags() ); } } /** * {@inheritdoc} */ public function canUserAssignTag(ISystemTag $tag, IUser $user) { // early check to avoid unneeded group lookups if ($tag->isUserAssignable() && $tag->isUserVisible()) { return true; } if ($this->groupManager->isAdmin($user->getUID())) { return true; } if (!$tag->isUserVisible()) { return false; } $groupIds = $this->groupManager->getUserGroupIds($user); if (!empty($groupIds)) { $matchingGroups = array_intersect($groupIds, $this->getTagGroups($tag)); if (!empty($matchingGroups)) { return true; } } return false; } /** * {@inheritdoc} */ public function canUserSeeTag(ISystemTag $tag, IUser $user) { if ($tag->isUserVisible()) { return true; } if ($this->groupManager->isAdmin($user->getUID())) { return true; } return false; } private function createSystemTagFromRow($row) { return new SystemTag((int)$row['id'], $row['name'], (bool)$row['visibility'], (bool)$row['editable']); } /** * {@inheritdoc} */ public function setTagGroups(ISystemTag $tag, $groupIds) { // delete relationships first $this->connection->beginTransaction(); try { $query = $this->connection->getQueryBuilder(); $query->delete(self::TAG_GROUP_TABLE) ->where($query->expr()->eq('systemtagid', $query->createNamedParameter($tag->getId()))) ->execute(); // add each group id $query = $this->connection->getQueryBuilder(); $query->insert(self::TAG_GROUP_TABLE) ->values([ 'systemtagid' => $query->createNamedParameter($tag->getId()), 'gid' => $query->createParameter('gid'), ]); foreach ($groupIds as $groupId) { if ($groupId === '') { continue; } $query->setParameter('gid', $groupId); $query->execute(); } $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollback(); throw $e; } } /** * {@inheritdoc} */ public function getTagGroups(ISystemTag $tag) { $groupIds = []; $query = $this->connection->getQueryBuilder(); $query->select('gid') ->from(self::TAG_GROUP_TABLE) ->where($query->expr()->eq('systemtagid', $query->createNamedParameter($tag->getId()))) ->orderBy('gid'); $result = $query->execute(); while ($row = $result->fetch()) { $groupIds[] = $row['gid']; } $result->closeCursor(); return $groupIds; } } private/SystemTag/SystemTagObjectMapper.php 0000604 00000016220 15247130451 0015062 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\SystemTag; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; use OCP\SystemTag\MapperEvent; use OCP\SystemTag\TagNotFoundException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class SystemTagObjectMapper implements ISystemTagObjectMapper { const RELATION_TABLE = 'systemtag_object_mapping'; /** @var ISystemTagManager */ protected $tagManager; /** @var IDBConnection */ protected $connection; /** @var EventDispatcherInterface */ protected $dispatcher; /** * Constructor. * * @param IDBConnection $connection database connection * @param ISystemTagManager $tagManager system tag manager * @param EventDispatcherInterface $dispatcher */ public function __construct(IDBConnection $connection, ISystemTagManager $tagManager, EventDispatcherInterface $dispatcher) { $this->connection = $connection; $this->tagManager = $tagManager; $this->dispatcher = $dispatcher; } /** * {@inheritdoc} */ public function getTagIdsForObjects($objIds, $objectType) { if (!is_array($objIds)) { $objIds = [$objIds]; } else if (empty($objIds)) { return []; } $query = $this->connection->getQueryBuilder(); $query->select(['systemtagid', 'objectid']) ->from(self::RELATION_TABLE) ->where($query->expr()->in('objectid', $query->createParameter('objectids'))) ->andWhere($query->expr()->eq('objecttype', $query->createParameter('objecttype'))) ->setParameter('objectids', $objIds, IQueryBuilder::PARAM_INT_ARRAY) ->setParameter('objecttype', $objectType) ->addOrderBy('objectid', 'ASC') ->addOrderBy('systemtagid', 'ASC'); $mapping = []; foreach ($objIds as $objId) { $mapping[$objId] = []; } $result = $query->execute(); while ($row = $result->fetch()) { $objectId = $row['objectid']; $mapping[$objectId][] = $row['systemtagid']; } $result->closeCursor(); return $mapping; } /** * {@inheritdoc} */ public function getObjectIdsForTags($tagIds, $objectType, $limit = 0, $offset = '') { if (!is_array($tagIds)) { $tagIds = [$tagIds]; } $this->assertTagsExist($tagIds); $query = $this->connection->getQueryBuilder(); $query->selectDistinct('objectid') ->from(self::RELATION_TABLE) ->where($query->expr()->in('systemtagid', $query->createNamedParameter($tagIds, IQueryBuilder::PARAM_INT_ARRAY))) ->andWhere($query->expr()->eq('objecttype', $query->createNamedParameter($objectType))); if ($limit) { if (sizeof($tagIds) !== 1) { throw new \InvalidArgumentException('Limit is only allowed with a single tag'); } $query->setMaxResults($limit) ->orderBy('objectid', 'ASC'); if ($offset !== '') { $query->andWhere($query->expr()->gt('objectid', $query->createNamedParameter($offset))); } } $objectIds = []; $result = $query->execute(); while ($row = $result->fetch()) { $objectIds[] = $row['objectid']; } return $objectIds; } /** * {@inheritdoc} */ public function assignTags($objId, $objectType, $tagIds) { if (!is_array($tagIds)) { $tagIds = [$tagIds]; } $this->assertTagsExist($tagIds); $query = $this->connection->getQueryBuilder(); $query->insert(self::RELATION_TABLE) ->values([ 'objectid' => $query->createNamedParameter($objId), 'objecttype' => $query->createNamedParameter($objectType), 'systemtagid' => $query->createParameter('tagid'), ]); foreach ($tagIds as $tagId) { try { $query->setParameter('tagid', $tagId); $query->execute(); } catch (UniqueConstraintViolationException $e) { // ignore existing relations } } $this->dispatcher->dispatch(MapperEvent::EVENT_ASSIGN, new MapperEvent( MapperEvent::EVENT_ASSIGN, $objectType, $objId, $tagIds )); } /** * {@inheritdoc} */ public function unassignTags($objId, $objectType, $tagIds) { if (!is_array($tagIds)) { $tagIds = [$tagIds]; } $this->assertTagsExist($tagIds); $query = $this->connection->getQueryBuilder(); $query->delete(self::RELATION_TABLE) ->where($query->expr()->eq('objectid', $query->createParameter('objectid'))) ->andWhere($query->expr()->eq('objecttype', $query->createParameter('objecttype'))) ->andWhere($query->expr()->in('systemtagid', $query->createParameter('tagids'))) ->setParameter('objectid', $objId) ->setParameter('objecttype', $objectType) ->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY) ->execute(); $this->dispatcher->dispatch(MapperEvent::EVENT_UNASSIGN, new MapperEvent( MapperEvent::EVENT_UNASSIGN, $objectType, $objId, $tagIds )); } /** * {@inheritdoc} */ public function haveTag($objIds, $objectType, $tagId, $all = true) { $this->assertTagsExist([$tagId]); if (!is_array($objIds)) { $objIds = [$objIds]; } $query = $this->connection->getQueryBuilder(); if (!$all) { // If we only need one entry, we make the query lighter, by not // counting the elements $query->select('*') ->setMaxResults(1); } else { $query->select($query->createFunction('COUNT(1)')); } $query->from(self::RELATION_TABLE) ->where($query->expr()->in('objectid', $query->createParameter('objectids'))) ->andWhere($query->expr()->eq('objecttype', $query->createParameter('objecttype'))) ->andWhere($query->expr()->eq('systemtagid', $query->createParameter('tagid'))) ->setParameter('objectids', $objIds, IQueryBuilder::PARAM_STR_ARRAY) ->setParameter('tagid', $tagId) ->setParameter('objecttype', $objectType); $result = $query->execute(); $row = $result->fetch(\PDO::FETCH_NUM); $result->closeCursor(); if ($all) { return ((int)$row[0] === count($objIds)); } else { return (bool) $row; } } /** * Asserts that all the given tag ids exist. * * @param string[] $tagIds tag ids to check * * @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist */ private function assertTagsExist($tagIds) { $tags = $this->tagManager->getTagsByIds($tagIds); if (count($tags) !== count($tagIds)) { // at least one tag missing, bail out $foundTagIds = array_map( function(ISystemTag $tag) { return $tag->getId(); }, $tags ); $missingTagIds = array_diff($tagIds, $foundTagIds); throw new TagNotFoundException( 'Tags not found', 0, null, $missingTagIds ); } } } private/Template/ResourceLocator.php 0000604 00000011444 15247130451 0013617 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Template; abstract class ResourceLocator { protected $theme; protected $mapping; protected $serverroot; protected $thirdpartyroot; protected $webroot; protected $resources = array(); /** @var \OCP\ILogger */ protected $logger; /** * @param \OCP\ILogger $logger * @param string $theme * @param array $core_map * @param array $party_map */ public function __construct(\OCP\ILogger $logger, $theme, $core_map, $party_map) { $this->logger = $logger; $this->theme = $theme; $this->mapping = $core_map + $party_map; $this->serverroot = key($core_map); $this->thirdpartyroot = key($party_map); $this->webroot = $this->mapping[$this->serverroot]; } /** * @param string $resource */ abstract public function doFind($resource); /** * @param string $resource */ abstract public function doFindTheme($resource); /** * Finds the resources and adds them to the list * * @param array $resources */ public function find($resources) { foreach ($resources as $resource) { try { $this->doFind($resource); } catch (ResourceNotFoundException $e) { $resourceApp = substr($resource, 0, strpos($resource, '/')); $this->logger->debug('Could not find resource file "' . $e->getResourcePath() . '"', ['app' => $resourceApp]); } } if (!empty($this->theme)) { foreach ($resources as $resource) { try { $this->doFindTheme($resource); } catch (ResourceNotFoundException $e) { $resourceApp = substr($resource, 0, strpos($resource, '/')); $this->logger->debug('Could not find resource file in theme "' . $e->getResourcePath() . '"', ['app' => $resourceApp]); } } } } /** * append the $file resource if exist at $root * * @param string $root path to check * @param string $file the filename * @param string|null $webRoot base for path, default map $root to $webRoot * @return bool True if the resource was found, false otherwise */ protected function appendIfExist($root, $file, $webRoot = null) { if (is_file($root.'/'.$file)) { $this->append($root, $file, $webRoot, false); return true; } return false; } /** * append the $file resource at $root * * @param string $root path to check * @param string $file the filename * @param string|null $webRoot base for path, default map $root to $webRoot * @param bool $throw Throw an exception, when the route does not exist * @throws ResourceNotFoundException Only thrown when $throw is true and the resource is missing */ protected function append($root, $file, $webRoot = null, $throw = true) { if (!is_string($root)) { if ($throw) { throw new ResourceNotFoundException($file, $webRoot); } return; } if (!$webRoot) { $tmpRoot = realpath($root); /* * traverse the potential web roots upwards in the path * * example: * - root: /srv/www/apps/myapp * - available mappings: ['/srv/www'] * * First we check if a mapping for /srv/www/apps/myapp is available, * then /srv/www/apps, /srv/www/apps, /srv/www, ... until we find a * valid web root */ do { if (isset($this->mapping[$tmpRoot])) { $webRoot = $this->mapping[$tmpRoot]; break; } if ($tmpRoot === '/') { $webRoot = ''; $this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [ 'app' => 'lib', 'root' => $root, 'file' => $file, 'webRoot' => $webRoot, 'throw' => $throw ? 'true' : 'false' ]); break; } $tmpRoot = dirname($tmpRoot); } while(true); } $this->resources[] = array($root, $webRoot, $file); if ($throw && !is_file($root . '/' . $file)) { throw new ResourceNotFoundException($file, $webRoot); } } /** * Returns the list of all resources that should be loaded * @return array */ public function getResources() { return $this->resources; } } private/Template/SCSSCacher.php 0000604 00000020660 15247130451 0012365 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, John Molakvoæ (skjnldsv@protonmail.com) * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Template; use Leafo\ScssPhp\Compiler; use Leafo\ScssPhp\Exception\ParserException; use Leafo\ScssPhp\Formatter\Crunched; use Leafo\ScssPhp\Formatter\Expanded; use OC\Files\AppData\Factory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\ICache; use OCP\IConfig; use OCP\ILogger; use OCP\IURLGenerator; class SCSSCacher { /** @var ILogger */ protected $logger; /** @var IAppData */ protected $appData; /** @var IURLGenerator */ protected $urlGenerator; /** @var IConfig */ protected $config; /** @var string */ protected $serverRoot; /** @var ICache */ protected $depsCache; /** * @param ILogger $logger * @param Factory $appDataFactory * @param IURLGenerator $urlGenerator * @param IConfig $config * @param \OC_Defaults $defaults * @param string $serverRoot * @param ICache $depsCache */ public function __construct(ILogger $logger, Factory $appDataFactory, IURLGenerator $urlGenerator, IConfig $config, \OC_Defaults $defaults, $serverRoot, ICache $depsCache) { $this->logger = $logger; $this->appData = $appDataFactory->get('css'); $this->urlGenerator = $urlGenerator; $this->config = $config; $this->defaults = $defaults; $this->serverRoot = $serverRoot; $this->depsCache = $depsCache; } /** * Process the caching process if needed * @param string $root Root path to the nextcloud installation * @param string $file * @param string $app The app name * @return boolean */ public function process($root, $file, $app) { $path = explode('/', $root . '/' . $file); $fileNameSCSS = array_pop($path); $fileNameCSS = $this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS)); $path = implode('/', $path); $webDir = substr($path, strlen($this->serverRoot)+1); try { $folder = $this->appData->getFolder($app); } catch(NotFoundException $e) { // creating css appdata folder $folder = $this->appData->newFolder($app); } if(!$this->variablesChanged() && $this->isCached($fileNameCSS, $folder)) { return true; } return $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir); } /** * @param $appName * @param $fileName * @return ISimpleFile */ public function getCachedCSS($appName, $fileName) { $folder = $this->appData->getFolder($appName); return $folder->getFile($this->prependBaseurlPrefix($fileName)); } /** * Check if the file is cached or not * @param string $fileNameCSS * @param ISimpleFolder $folder * @return boolean */ private function isCached($fileNameCSS, ISimpleFolder $folder) { try { $cachedFile = $folder->getFile($fileNameCSS); if ($cachedFile->getSize() > 0) { $depFileName = $fileNameCSS . '.deps'; $deps = $this->depsCache->get($folder->getName() . '-' . $depFileName); if ($deps === null) { $depFile = $folder->getFile($depFileName); $deps = $depFile->getContent(); //Set to memcache for next run $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps); } $deps = json_decode($deps, true); foreach ($deps as $file=>$mtime) { if (!file_exists($file) || filemtime($file) > $mtime) { return false; } } } return true; } catch(NotFoundException $e) { return false; } } /** * Check if the variables file has changed * @return bool */ private function variablesChanged() { $injectedVariables = $this->getInjectedVariables(); if($this->config->getAppValue('core', 'scss.variables') !== md5($injectedVariables)) { $this->resetCache(); $this->config->setAppValue('core', 'scss.variables', md5($injectedVariables)); return true; } return false; } /** * Cache the file with AppData * @param string $path * @param string $fileNameCSS * @param string $fileNameSCSS * @param ISimpleFolder $folder * @param string $webDir * @return boolean */ private function cache($path, $fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $webDir) { $scss = new Compiler(); $scss->setImportPaths([ $path, \OC::$SERVERROOT . '/core/css/', ]); if($this->config->getSystemValue('debug')) { // Debug mode $scss->setFormatter(Expanded::class); $scss->setLineNumberStyle(Compiler::LINE_COMMENTS); } else { // Compression $scss->setFormatter(Crunched::class); } try { $cachedfile = $folder->getFile($fileNameCSS); } catch(NotFoundException $e) { $cachedfile = $folder->newFile($fileNameCSS); } $depFileName = $fileNameCSS . '.deps'; try { $depFile = $folder->getFile($depFileName); } catch (NotFoundException $e) { $depFile = $folder->newFile($depFileName); } // Compile try { $compiledScss = $scss->compile( '@import "variables.scss";' . $this->getInjectedVariables() . '@import "'.$fileNameSCSS.'";'); } catch(ParserException $e) { $this->logger->error($e, ['app' => 'core']); return false; } // Gzip file try { $gzipFile = $folder->getFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz } catch (NotFoundException $e) { $gzipFile = $folder->newFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz } try { $data = $this->rebaseUrls($compiledScss, $webDir); $cachedfile->putContent($data); $deps = json_encode($scss->getParsedFiles()); $depFile->putContent($deps); $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps); $gzipFile->putContent(gzencode($data, 9)); $this->logger->debug($webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'core']); return true; } catch(NotPermittedException $e) { return false; } } /** * Reset scss cache by deleting all generated css files * We need to regenerate all files when variables change */ private function resetCache() { $appDirectory = $this->appData->getDirectoryListing(); if(empty($appDirectory)){ return; } foreach ($appDirectory as $folder) { foreach ($folder->getDirectoryListing() as $file) { if (substr($file->getName(), -3) === "css" || substr($file->getName(), -4) === "deps") { $file->delete(); } } } } /** * @return string SCSS code for variables from OC_Defaults */ private function getInjectedVariables() { $variables = ''; foreach ($this->defaults->getScssVariables() as $key => $value) { $variables .= '$' . $key . ': ' . $value . ';'; } return $variables; } /** * Add the correct uri prefix to make uri valid again * @param string $css * @param string $webDir * @return string */ private function rebaseUrls($css, $webDir) { $re = '/url\([\'"]([\.\w?=\/-]*)[\'"]\)/x'; // OC\Route\Router:75 if(($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) { $subst = 'url(\'../../'.$webDir.'/$1\')'; } else { $subst = 'url(\'../../../'.$webDir.'/$1\')'; } return preg_replace($re, $subst, $css); } /** * Return the cached css file uri * @param string $appName the app name * @param string $fileName * @return string */ public function getCachedSCSS($appName, $fileName) { $tmpfileLoc = explode('/', $fileName); $fileName = array_pop($tmpfileLoc); $fileName = $this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileName)); return substr($this->urlGenerator->linkToRoute('core.Css.getCss', array('fileName' => $fileName, 'appName' => $appName)), strlen(\OC::$WEBROOT) + 1); } /** * Prepend hashed base url to the css file * @param $cssFile * @return string */ private function prependBaseurlPrefix($cssFile) { return md5($this->urlGenerator->getBaseUrl()) . '-' . $cssFile; } } private/Template/CSSResourceLocator.php 0000604 00000011414 15247130451 0014165 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Template; use OCP\ILogger; class CSSResourceLocator extends ResourceLocator { /** @var SCSSCacher */ protected $scssCacher; /** * @param ILogger $logger * @param string $theme * @param array $core_map * @param array $party_map * @param SCSSCacher $scssCacher */ public function __construct(ILogger $logger, $theme, $core_map, $party_map, $scssCacher) { $this->scssCacher = $scssCacher; parent::__construct($logger, $theme, $core_map, $party_map); } /** * @param string $style */ public function doFind($style) { $app = substr($style, 0, strpos($style, '/')); if (strpos($style, '3rdparty') === 0 && $this->appendIfExist($this->thirdpartyroot, $style.'.css') || $this->cacheAndAppendScssIfExist($this->serverroot, $style.'.scss', $app) || $this->cacheAndAppendScssIfExist($this->serverroot, 'core/'.$style.'.scss') || $this->appendIfExist($this->serverroot, $style.'.css') || $this->appendIfExist($this->serverroot, 'core/'.$style.'.css') ) { return; } $style = substr($style, strpos($style, '/')+1); $app_path = \OC_App::getAppPath($app); $app_url = \OC_App::getAppWebPath($app); if ($app_path === false && $app_url === false) { $this->logger->error('Could not find resource {resource} to load', [ 'resource' => $app . '/' . $style . '.css', 'app' => 'cssresourceloader', ]); return; } if(!$this->cacheAndAppendScssIfExist($app_path, $style.'.scss', $app)) { $this->append($app_path, $style.'.css', $app_url); } } /** * @param string $style */ public function doFindTheme($style) { $theme_dir = 'themes/'.$this->theme.'/'; $this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$style.'.css') || $this->appendIfExist($this->serverroot, $theme_dir.$style.'.css') || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$style.'.css'); } /** * cache and append the scss $file if exist at $root * * @param string $root path to check * @param string $file the filename * @return bool True if the resource was found and cached, false otherwise */ protected function cacheAndAppendScssIfExist($root, $file, $app = 'core') { if (is_file($root.'/'.$file)) { if($this->scssCacher !== null) { if($this->scssCacher->process($root, $file, $app)) { $this->append($root, $this->scssCacher->getCachedSCSS($app, $file), false, true, true); return true; } else { $this->logger->warning('Failed to compile and/or save '.$root.'/'.$file, ['app' => 'core']); return false; } } else { $this->logger->debug('Scss is disabled for '.$root.'/'.$file.', ignoring', ['app' => 'core']); return true; } } return false; } public function append($root, $file, $webRoot = null, $throw = true, $scss = false) { if (!$scss) { parent::append($root, $file, $webRoot, $throw); } else { if (!$webRoot) { $tmpRoot = realpath($root); /* * traverse the potential web roots upwards in the path * * example: * - root: /srv/www/apps/myapp * - available mappings: ['/srv/www'] * * First we check if a mapping for /srv/www/apps/myapp is available, * then /srv/www/apps, /srv/www/apps, /srv/www, ... until we find a * valid web root */ do { if (isset($this->mapping[$tmpRoot])) { $webRoot = $this->mapping[$tmpRoot]; break; } if ($tmpRoot === '/') { $webRoot = ''; $this->logger->error('ResourceLocator can not find a web root (root: {root}, file: {file}, webRoot: {webRoot}, throw: {throw})', [ 'app' => 'lib', 'root' => $root, 'file' => $file, 'webRoot' => $webRoot, 'throw' => $throw ? 'true' : 'false' ]); break; } $tmpRoot = dirname($tmpRoot); } while(true); } if ($throw && $tmpRoot === '/') { throw new ResourceNotFoundException($file, $webRoot); } $this->resources[] = array($tmpRoot, $webRoot, $file); } } } private/Template/ResourceNotFoundException.php 0000604 00000002371 15247130451 0015626 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Template; class ResourceNotFoundException extends \LogicException { protected $resource; protected $webPath; /** * @param string $resource * @param string $webPath */ public function __construct($resource, $webPath) { parent::__construct('Resource not found'); $this->resource = $resource; $this->webPath = $webPath; } /** * @return string */ public function getResourcePath() { return $this->webPath . '/' . $this->resource; } } private/Template/JSConfigHelper.php 0000604 00000022345 15247130451 0013310 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Template; use bantu\IniGetWrapper\IniGetWrapper; use OCP\App\IAppManager; use OCP\Defaults; use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\ISession; use OCP\IURLGenerator; use OCP\IUser; class JSConfigHelper { /** @var IL10N */ private $l; /** @var Defaults */ private $defaults; /** @var IAppManager */ private $appManager; /** @var ISession */ private $session; /** @var IUser|null */ private $currentUser; /** @var IConfig */ private $config; /** @var IGroupManager */ private $groupManager; /** @var IniGetWrapper */ private $iniWrapper; /** @var IURLGenerator */ private $urlGenerator; /** * @param IL10N $l * @param Defaults $defaults * @param IAppManager $appManager * @param ISession $session * @param IUser|null $currentUser * @param IConfig $config * @param IGroupManager $groupManager * @param IniGetWrapper $iniWrapper * @param IURLGenerator $urlGenerator */ public function __construct(IL10N $l, Defaults $defaults, IAppManager $appManager, ISession $session, $currentUser, IConfig $config, IGroupManager $groupManager, IniGetWrapper $iniWrapper, IURLGenerator $urlGenerator) { $this->l = $l; $this->defaults = $defaults; $this->appManager = $appManager; $this->session = $session; $this->currentUser = $currentUser; $this->config = $config; $this->groupManager = $groupManager; $this->iniWrapper = $iniWrapper; $this->urlGenerator = $urlGenerator; } public function getConfig() { if ($this->currentUser !== null) { $uid = $this->currentUser->getUID(); } else { $uid = null; } // Get the config $apps_paths = []; if ($this->currentUser === null) { $apps = $this->appManager->getInstalledApps(); } else { $apps = $this->appManager->getEnabledAppsForUser($this->currentUser); } foreach($apps as $app) { $apps_paths[$app] = \OC_App::getAppWebPath($app); } $enableLinkPasswordByDefault = $this->config->getAppValue('core', 'shareapi_enable_link_password_by_default', 'no'); $enableLinkPasswordByDefault = ($enableLinkPasswordByDefault === 'yes') ? true : false; $defaultExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes'; $defaultExpireDate = $enforceDefaultExpireDate = null; if ($defaultExpireDateEnabled) { $defaultExpireDate = (int) $this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'); $enforceDefaultExpireDate = $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes'; } $outgoingServer2serverShareEnabled = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes'; $countOfDataLocation = 0; $dataLocation = str_replace(\OC::$SERVERROOT .'/', '', $this->config->getSystemValue('datadirectory', ''), $countOfDataLocation); if($countOfDataLocation !== 1 || !$this->groupManager->isAdmin($uid)) { $dataLocation = false; } if ($this->currentUser instanceof IUser) { $lastConfirmTimestamp = $this->session->get('last-password-confirm'); if (!is_int($lastConfirmTimestamp)) { $lastConfirmTimestamp = 0; } } else { $lastConfirmTimestamp = 0; } $array = [ "oc_debug" => $this->config->getSystemValue('debug', false) ? 'true' : 'false', "oc_isadmin" => $this->groupManager->isAdmin($uid) ? 'true' : 'false', "oc_dataURL" => is_string($dataLocation) ? "\"".$dataLocation."\"" : 'false', "oc_webroot" => "\"".\OC::$WEBROOT."\"", "oc_appswebroots" => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution "datepickerFormatDate" => json_encode($this->l->l('jsdate', null)), 'nc_lastLogin' => $lastConfirmTimestamp, "dayNames" => json_encode([ (string)$this->l->t('Sunday'), (string)$this->l->t('Monday'), (string)$this->l->t('Tuesday'), (string)$this->l->t('Wednesday'), (string)$this->l->t('Thursday'), (string)$this->l->t('Friday'), (string)$this->l->t('Saturday') ]), "dayNamesShort" => json_encode([ (string)$this->l->t('Sun.'), (string)$this->l->t('Mon.'), (string)$this->l->t('Tue.'), (string)$this->l->t('Wed.'), (string)$this->l->t('Thu.'), (string)$this->l->t('Fri.'), (string)$this->l->t('Sat.') ]), "dayNamesMin" => json_encode([ (string)$this->l->t('Su'), (string)$this->l->t('Mo'), (string)$this->l->t('Tu'), (string)$this->l->t('We'), (string)$this->l->t('Th'), (string)$this->l->t('Fr'), (string)$this->l->t('Sa') ]), "monthNames" => json_encode([ (string)$this->l->t('January'), (string)$this->l->t('February'), (string)$this->l->t('March'), (string)$this->l->t('April'), (string)$this->l->t('May'), (string)$this->l->t('June'), (string)$this->l->t('July'), (string)$this->l->t('August'), (string)$this->l->t('September'), (string)$this->l->t('October'), (string)$this->l->t('November'), (string)$this->l->t('December') ]), "monthNamesShort" => json_encode([ (string)$this->l->t('Jan.'), (string)$this->l->t('Feb.'), (string)$this->l->t('Mar.'), (string)$this->l->t('Apr.'), (string)$this->l->t('May.'), (string)$this->l->t('Jun.'), (string)$this->l->t('Jul.'), (string)$this->l->t('Aug.'), (string)$this->l->t('Sep.'), (string)$this->l->t('Oct.'), (string)$this->l->t('Nov.'), (string)$this->l->t('Dec.') ]), "firstDay" => json_encode($this->l->l('firstday', null)) , "oc_config" => json_encode([ 'session_lifetime' => min($this->config->getSystemValue('session_lifetime', $this->iniWrapper->getNumeric('session.gc_maxlifetime')), $this->iniWrapper->getNumeric('session.gc_maxlifetime')), 'session_keepalive' => $this->config->getSystemValue('session_keepalive', true), 'version' => implode('.', \OCP\Util::getVersion()), 'versionstring' => \OC_Util::getVersionString(), 'enable_avatars' => true, // here for legacy reasons - to not crash existing code that relies on this value 'lost_password_link'=> $this->config->getSystemValue('lost_password_link', null), 'modRewriteWorking' => ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'), 'sharing.maxAutocompleteResults' => intval($this->config->getSystemValue('sharing.maxAutocompleteResults', 0)), 'sharing.minSearchStringLength' => intval($this->config->getSystemValue('sharing.minSearchStringLength', 0)), 'blacklist_files_regex' => \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX, ]), "oc_appconfig" => json_encode([ 'core' => [ 'defaultExpireDateEnabled' => $defaultExpireDateEnabled, 'defaultExpireDate' => $defaultExpireDate, 'defaultExpireDateEnforced' => $enforceDefaultExpireDate, 'enforcePasswordForPublicLink' => \OCP\Util::isPublicLinkPasswordRequired(), 'enableLinkPasswordByDefault' => $enableLinkPasswordByDefault, 'sharingDisabledForUser' => \OCP\Util::isSharingDisabledForUser(), 'resharingAllowed' => \OCP\Share::isResharingAllowed(), 'remoteShareAllowed' => $outgoingServer2serverShareEnabled, 'federatedCloudShareDoc' => $this->urlGenerator->linkToDocs('user-sharing-federated'), 'allowGroupSharing' => \OC::$server->getShareManager()->allowGroupSharing() ] ]), "oc_defaults" => json_encode([ 'entity' => $this->defaults->getEntity(), 'name' => $this->defaults->getName(), 'title' => $this->defaults->getTitle(), 'baseUrl' => $this->defaults->getBaseUrl(), 'syncClientUrl' => $this->defaults->getSyncClientUrl(), 'docBaseUrl' => $this->defaults->getDocBaseUrl(), 'docPlaceholderUrl' => $this->defaults->buildDocLinkToKey('PLACEHOLDER'), 'slogan' => $this->defaults->getSlogan(), 'logoClaim' => $this->defaults->getLogoClaim(), 'shortFooter' => $this->defaults->getShortFooter(), 'longFooter' => $this->defaults->getLongFooter(), 'folder' => \OC_Util::getTheme(), ]), ]; if ($this->currentUser !== null) { $array['oc_userconfig'] = json_encode([ 'avatar' => [ 'version' => (int)$this->config->getUserValue($uid, 'avatar', 'version', 0), ] ]); } // Allow hooks to modify the output values \OC_Hook::emit('\OCP\Config', 'js', array('array' => &$array)); $result = ''; // Echo it foreach ($array as $setting => $value) { $result .= 'var '. $setting . '='. $value . ';' . PHP_EOL; } return $result; } } private/Template/JSResourceLocator.php 0000604 00000007725 15247130451 0014063 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Template; class JSResourceLocator extends ResourceLocator { /** @var JSCombiner */ protected $jsCombiner; public function __construct(\OCP\ILogger $logger, $theme, array $core_map, array $party_map, JSCombiner $JSCombiner) { parent::__construct($logger, $theme, $core_map, $party_map); $this->jsCombiner = $JSCombiner; } /** * @param string $script */ public function doFind($script) { $theme_dir = 'themes/'.$this->theme.'/'; if (strpos($script, '3rdparty') === 0 && $this->appendIfExist($this->thirdpartyroot, $script.'.js')) { return; } if (strpos($script, '/l10n/') !== false) { // For language files we try to load them all, so themes can overwrite // single l10n strings without having to translate all of them. $found = 0; $found += $this->appendIfExist($this->serverroot, 'core/'.$script.'.js'); $found += $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$script.'.js'); $found += $this->appendIfExist($this->serverroot, $script.'.js'); $found += $this->appendIfExist($this->serverroot, $theme_dir.$script.'.js'); $found += $this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$script.'.js'); if ($found) { return; } } else if ($this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$script.'.js') || $this->appendIfExist($this->serverroot, $theme_dir.$script.'.js') || $this->appendIfExist($this->serverroot, $script.'.js') || $this->cacheAndAppendCombineJsonIfExist($this->serverroot, $script.'.json') || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$script.'.js') || $this->appendIfExist($this->serverroot, 'core/'.$script.'.js') || $this->cacheAndAppendCombineJsonIfExist($this->serverroot, 'core/'.$script.'.json') ) { return; } $app = substr($script, 0, strpos($script, '/')); $script = substr($script, strpos($script, '/')+1); $app_path = \OC_App::getAppPath($app); $app_url = \OC_App::getAppWebPath($app); // missing translations files fill be ignored if (strpos($script, 'l10n/') === 0) { $this->appendIfExist($app_path, $script . '.js', $app_url); return; } if ($app_path === false && $app_url === false) { $this->logger->error('Could not find resource {resource} to load', [ 'resource' => $app . '/' . $script . '.js', 'app' => 'jsresourceloader', ]); return; } if (!$this->cacheAndAppendCombineJsonIfExist($app_path, $script.'.json', $app)) { $this->append($app_path, $script . '.js', $app_url); } } /** * @param string $script */ public function doFindTheme($script) { } protected function cacheAndAppendCombineJsonIfExist($root, $file, $app = 'core') { if (is_file($root.'/'.$file)) { if ($this->jsCombiner->process($root, $file, $app)) { $this->append($this->serverroot, $this->jsCombiner->getCachedJS($app, $file), false, false); } else { // Add all the files from the json $files = $this->jsCombiner->getContent($root, $file); $app_url = \OC_App::getAppWebPath($app); foreach ($files as $jsFile) { $this->append($root, $jsFile, $app_url); } } return true; } return false; } } private/Template/Base.php 0000604 00000010610 15247130451 0011350 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Template; use OCP\Defaults; class Base { private $template; // The template private $vars; // Vars /** @var \OCP\IL10N */ private $l10n; /** @var Defaults */ private $theme; /** * @param string $template * @param string $requestToken * @param \OCP\IL10N $l10n * @param Defaults $theme */ public function __construct($template, $requestToken, $l10n, $theme ) { $this->vars = array(); $this->vars['requesttoken'] = $requestToken; $this->l10n = $l10n; $this->template = $template; $this->theme = $theme; } /** * @param string $serverRoot * @param string|false $app_dir * @param string $theme * @param string $app * @return string[] */ protected function getAppTemplateDirs($theme, $app, $serverRoot, $app_dir) { // Check if the app is in the app folder or in the root if( file_exists($app_dir.'/templates/' )) { return [ $serverRoot.'/themes/'.$theme.'/apps/'.$app.'/templates/', $app_dir.'/templates/', ]; } return [ $serverRoot.'/themes/'.$theme.'/'.$app.'/templates/', $serverRoot.'/'.$app.'/templates/', ]; } /** * @param string $serverRoot * @param string $theme * @return string[] */ protected function getCoreTemplateDirs($theme, $serverRoot) { return [ $serverRoot.'/themes/'.$theme.'/core/templates/', $serverRoot.'/core/templates/', ]; } /** * Assign variables * @param string $key key * @param array|bool|integer|string $value value * @return bool * * This function assigns a variable. It can be accessed via $_[$key] in * the template. * * If the key existed before, it will be overwritten */ public function assign( $key, $value) { $this->vars[$key] = $value; return true; } /** * Appends a variable * @param string $key key * @param mixed $value value * @return boolean|null * * This function assigns a variable in an array context. If the key already * exists, the value will be appended. It can be accessed via * $_[$key][$position] in the template. */ public function append( $key, $value ) { if( array_key_exists( $key, $this->vars )) { $this->vars[$key][] = $value; } else{ $this->vars[$key] = array( $value ); } } /** * Prints the proceeded template * @return bool * * This function proceeds the template and prints its output. */ public function printPage() { $data = $this->fetchPage(); if( $data === false ) { return false; } else{ print $data; return true; } } /** * Process the template * * @param array|null $additionalParams * @return string This function processes the template. * * This function processes the template. */ public function fetchPage($additionalParams = null) { return $this->load($this->template, $additionalParams); } /** * doing the actual work * * @param string $file * @param array|null $additionalParams * @return string content * * Includes the template file, fetches its output */ protected function load($file, $additionalParams = null) { // Register the variables $_ = $this->vars; $l = $this->l10n; $theme = $this->theme; if( !is_null($additionalParams)) { $_ = array_merge( $additionalParams, $this->vars ); } // Include ob_start(); try { include $file; $data = ob_get_contents(); } catch (\Exception $e) { @ob_end_clean(); throw $e; } @ob_end_clean(); // Return data return $data; } } private/Template/JSCombiner.php 0000604 00000012324 15247130451 0012475 0 ustar 00 <?php /** * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Template; use OC\SystemConfig; use OCP\ICache; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IURLGenerator; class JSCombiner { /** @var IAppData */ protected $appData; /** @var IURLGenerator */ protected $urlGenerator; /** @var ICache */ protected $depsCache; /** @var SystemConfig */ protected $config; /** * @param IAppData $appData * @param IURLGenerator $urlGenerator * @param ICache $depsCache * @param SystemConfig $config */ public function __construct(IAppData $appData, IURLGenerator $urlGenerator, ICache $depsCache, SystemConfig $config) { $this->appData = $appData; $this->urlGenerator = $urlGenerator; $this->depsCache = $depsCache; $this->config = $config; } /** * @param string $root * @param string $file * @param string $app * @return bool */ public function process($root, $file, $app) { if ($this->config->getValue('debug') || !$this->config->getValue('installed')) { return false; } $path = explode('/', $root . '/' . $file); $fileName = array_pop($path); $path = implode('/', $path); try { $folder = $this->appData->getFolder($app); } catch(NotFoundException $e) { // creating css appdata folder $folder = $this->appData->newFolder($app); } if($this->isCached($fileName, $folder)) { return true; } return $this->cache($path, $fileName, $folder); } /** * @param string $fileName * @param ISimpleFolder $folder * @return bool */ protected function isCached($fileName, ISimpleFolder $folder) { $fileName = str_replace('.json', '.js', $fileName) . '.deps'; try { $deps = $this->depsCache->get($folder->getName() . '-' . $fileName); if ($deps === null || $deps === '') { $depFile = $folder->getFile($fileName); $deps = $depFile->getContent(); } $deps = json_decode($deps, true); foreach ($deps as $file=>$mtime) { if (!file_exists($file) || filemtime($file) > $mtime) { return false; } } return true; } catch(NotFoundException $e) { return false; } } /** * @param string $path * @param string $fileName * @param ISimpleFolder $folder * @return bool */ protected function cache($path, $fileName, ISimpleFolder $folder) { $deps = []; $fullPath = $path . '/' . $fileName; $data = json_decode(file_get_contents($fullPath)); $deps[$fullPath] = filemtime($fullPath); $res = ''; foreach ($data as $file) { $filePath = $path . '/' . $file; if (is_file($filePath)) { $res .= file_get_contents($filePath); $res .= PHP_EOL . PHP_EOL; $deps[$filePath] = filemtime($filePath); } } $fileName = str_replace('.json', '.js', $fileName); try { $cachedfile = $folder->getFile($fileName); } catch(NotFoundException $e) { $cachedfile = $folder->newFile($fileName); } $depFileName = $fileName . '.deps'; try { $depFile = $folder->getFile($depFileName); } catch (NotFoundException $e) { $depFile = $folder->newFile($depFileName); } try { $gzipFile = $folder->getFile($fileName . '.gzip'); # Safari doesn't like .gz } catch (NotFoundException $e) { $gzipFile = $folder->newFile($fileName . '.gzip'); # Safari doesn't like .gz } try { $cachedfile->putContent($res); $deps = json_encode($deps); $depFile->putContent($deps); $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps); $gzipFile->putContent(gzencode($res, 9)); return true; } catch (NotPermittedException $e) { return false; } } /** * @param string $appName * @param string $fileName * @return string */ public function getCachedJS($appName, $fileName) { $tmpfileLoc = explode('/', $fileName); $fileName = array_pop($tmpfileLoc); $fileName = str_replace('.json', '.js', $fileName); return substr($this->urlGenerator->linkToRoute('core.Js.getJs', array('fileName' => $fileName, 'appName' => $appName)), strlen(\OC::$WEBROOT) + 1); } /** * @param string $root * @param string $file * @return string[] */ public function getContent($root, $file) { /** @var array $data */ $data = json_decode(file_get_contents($root . '/' . $file)); if(!is_array($data)) { return []; } $path = explode('/', $file); array_pop($path); $path = implode('/', $path); $result = []; foreach ($data as $f) { $result[] = $path . '/' . $f; } return $result; } } private/Template/TemplateFileLocator.php 0000604 00000003047 15247130451 0014403 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Template; class TemplateFileLocator { protected $dirs; private $path; /** * @param string[] $dirs */ public function __construct( $dirs ) { $this->dirs = $dirs; } /** * @param string $template * @return string * @throws \Exception */ public function find( $template ) { if ($template === '') { throw new \InvalidArgumentException('Empty template name'); } foreach($this->dirs as $dir) { $file = $dir.$template.'.php'; if (is_file($file)) { $this->path = $dir; return $file; } } throw new \Exception('template file not found: template:'.$template); } public function getPath() { return $this->path; } } private/TempManager.php 0000604 00000016471 15247130451 0011136 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lars <winnetou+github@catolic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Martin Mattel <martin.mattel@diemattels.at> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OCP\ILogger; use OCP\IConfig; use OCP\ITempManager; class TempManager implements ITempManager { /** @var string[] Current temporary files and folders, used for cleanup */ protected $current = []; /** @var string i.e. /tmp on linux systems */ protected $tmpBaseDir; /** @var ILogger */ protected $log; /** @var IConfig */ protected $config; /** Prefix */ const TMP_PREFIX = 'oc_tmp_'; /** * @param \OCP\ILogger $logger * @param \OCP\IConfig $config */ public function __construct(ILogger $logger, IConfig $config) { $this->log = $logger; $this->config = $config; $this->tmpBaseDir = $this->getTempBaseDir(); } /** * Builds the filename with suffix and removes potential dangerous characters * such as directory separators. * * @param string $absolutePath Absolute path to the file / folder * @param string $postFix Postfix appended to the temporary file name, may be user controlled * @return string */ private function buildFileNameWithSuffix($absolutePath, $postFix = '') { if($postFix !== '') { $postFix = '.' . ltrim($postFix, '.'); $postFix = str_replace(['\\', '/'], '', $postFix); $absolutePath .= '-'; } return $absolutePath . $postFix; } /** * Create a temporary file and return the path * * @param string $postFix Postfix appended to the temporary file name * @return string */ public function getTemporaryFile($postFix = '') { if (is_writable($this->tmpBaseDir)) { // To create an unique file and prevent the risk of race conditions // or duplicated temporary files by other means such as collisions // we need to create the file using `tempnam` and append a possible // postfix to it later $file = tempnam($this->tmpBaseDir, self::TMP_PREFIX); $this->current[] = $file; // If a postfix got specified sanitize it and create a postfixed // temporary file if($postFix !== '') { $fileNameWithPostfix = $this->buildFileNameWithSuffix($file, $postFix); touch($fileNameWithPostfix); chmod($fileNameWithPostfix, 0600); $this->current[] = $fileNameWithPostfix; return $fileNameWithPostfix; } return $file; } else { $this->log->warning( 'Can not create a temporary file in directory {dir}. Check it exists and has correct permissions', [ 'dir' => $this->tmpBaseDir, ] ); return false; } } /** * Create a temporary folder and return the path * * @param string $postFix Postfix appended to the temporary folder name * @return string */ public function getTemporaryFolder($postFix = '') { if (is_writable($this->tmpBaseDir)) { // To create an unique directory and prevent the risk of race conditions // or duplicated temporary files by other means such as collisions // we need to create the file using `tempnam` and append a possible // postfix to it later $uniqueFileName = tempnam($this->tmpBaseDir, self::TMP_PREFIX); $this->current[] = $uniqueFileName; // Build a name without postfix $path = $this->buildFileNameWithSuffix($uniqueFileName . '-folder', $postFix); mkdir($path, 0700); $this->current[] = $path; return $path . '/'; } else { $this->log->warning( 'Can not create a temporary folder in directory {dir}. Check it exists and has correct permissions', [ 'dir' => $this->tmpBaseDir, ] ); return false; } } /** * Remove the temporary files and folders generated during this request */ public function clean() { $this->cleanFiles($this->current); } /** * @param string[] $files */ protected function cleanFiles($files) { foreach ($files as $file) { if (file_exists($file)) { try { \OC_Helper::rmdirr($file); } catch (\UnexpectedValueException $ex) { $this->log->warning( "Error deleting temporary file/folder: {file} - Reason: {error}", [ 'file' => $file, 'error' => $ex->getMessage(), ] ); } } } } /** * Remove old temporary files and folders that were failed to be cleaned */ public function cleanOld() { $this->cleanFiles($this->getOldFiles()); } /** * Get all temporary files and folders generated by oc older than an hour * * @return string[] */ protected function getOldFiles() { $cutOfTime = time() - 3600; $files = []; $dh = opendir($this->tmpBaseDir); if ($dh) { while (($file = readdir($dh)) !== false) { if (substr($file, 0, 7) === self::TMP_PREFIX) { $path = $this->tmpBaseDir . '/' . $file; $mtime = filemtime($path); if ($mtime < $cutOfTime) { $files[] = $path; } } } } return $files; } /** * Get the temporary base directory configured on the server * * @return string Path to the temporary directory or null * @throws \UnexpectedValueException */ public function getTempBaseDir() { if ($this->tmpBaseDir) { return $this->tmpBaseDir; } $directories = []; if ($temp = $this->config->getSystemValue('tempdirectory', null)) { $directories[] = $temp; } if ($temp = \OC::$server->getIniWrapper()->get('upload_tmp_dir')) { $directories[] = $temp; } if ($temp = getenv('TMP')) { $directories[] = $temp; } if ($temp = getenv('TEMP')) { $directories[] = $temp; } if ($temp = getenv('TMPDIR')) { $directories[] = $temp; } if ($temp = sys_get_temp_dir()) { $directories[] = $temp; } foreach ($directories as $dir) { if ($this->checkTemporaryDirectory($dir)) { return $dir; } } $temp = tempnam(dirname(__FILE__), ''); if (file_exists($temp)) { unlink($temp); return dirname($temp); } throw new \UnexpectedValueException('Unable to detect system temporary directory'); } /** * Check if a temporary directory is ready for use * * @param mixed $directory * @return bool */ private function checkTemporaryDirectory($directory) { // suppress any possible errors caused by is_writable // checks missing or invalid path or characters, wrong permissions etc try { if (is_writeable($directory)) { return true; } } catch (\Exception $e) { } $this->log->warning('Temporary directory {dir} is not present or writable', ['dir' => $directory] ); return false; } /** * Override the temporary base directory * * @param string $directory */ public function overrideTempBaseDir($directory) { $this->tmpBaseDir = $directory; } } private/Group/Backend.php 0000604 00000006371 15247130451 0011357 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Group; /** * Abstract base class for user management */ abstract class Backend implements \OCP\GroupInterface { /** * error code for functions not provided by the group backend */ const NOT_IMPLEMENTED = -501; protected $possibleActions = [ self::CREATE_GROUP => 'createGroup', self::DELETE_GROUP => 'deleteGroup', self::ADD_TO_GROUP => 'addToGroup', self::REMOVE_FROM_GOUP => 'removeFromGroup', self::COUNT_USERS => 'countUsersInGroup', self::GROUP_DETAILS => 'getGroupDetails', ]; /** * Get all supported actions * @return int bitwise-or'ed actions * * Returns the supported actions as int to be * compared with \OC\Group\Backend::CREATE_GROUP etc. */ public function getSupportedActions() { $actions = 0; foreach($this->possibleActions AS $action => $methodName) { if(method_exists($this, $methodName)) { $actions |= $action; } } return $actions; } /** * Check if backend implements actions * @param int $actions bitwise-or'ed actions * @return bool * * Returns the supported actions as int to be * compared with \OC\Group\Backend::CREATE_GROUP etc. */ public function implementsActions($actions) { return (bool)($this->getSupportedActions() & $actions); } /** * is user in group? * @param string $uid uid of the user * @param string $gid gid of the group * @return bool * * Checks whether the user is member of a group or not. */ public function inGroup($uid, $gid) { return in_array($gid, $this->getUserGroups($uid)); } /** * Get all groups a user belongs to * @param string $uid Name of the user * @return array an array of group names * * This function fetches all groups a user belongs to. It does not check * if the user exists at all. */ public function getUserGroups($uid) { return array(); } /** * get a list of all groups * @param string $search * @param int $limit * @param int $offset * @return array an array of group names * * Returns a list with all groups */ public function getGroups($search = '', $limit = -1, $offset = 0) { return array(); } /** * check if a group exists * @param string $gid * @return bool */ public function groupExists($gid) { return in_array($gid, $this->getGroups($gid, 1)); } /** * get a list of all users in a group * @param string $gid * @param string $search * @param int $limit * @param int $offset * @return array an array of user ids */ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { return array(); } } private/Group/Database.php 0000604 00000020356 15247130451 0011533 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Aaron Wood <aaronjwood@gmail.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /* * * The following SQL statement is just a help for developers and will not be * executed! * * CREATE TABLE `groups` ( * `gid` varchar(64) COLLATE utf8_unicode_ci NOT NULL, * PRIMARY KEY (`gid`) * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; * * CREATE TABLE `group_user` ( * `gid` varchar(64) COLLATE utf8_unicode_ci NOT NULL, * `uid` varchar(64) COLLATE utf8_unicode_ci NOT NULL * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; * */ namespace OC\Group; /** * Class for group management in a SQL Database (e.g. MySQL, SQLite) */ class Database extends \OC\Group\Backend { /** @var string[] */ private $groupCache = []; /** @var \OCP\IDBConnection */ private $dbConn; /** * \OC\Group\Database constructor. * * @param \OCP\IDBConnection|null $dbConn */ public function __construct(\OCP\IDBConnection $dbConn = null) { $this->dbConn = $dbConn; } /** * FIXME: This function should not be required! */ private function fixDI() { if ($this->dbConn === null) { $this->dbConn = \OC::$server->getDatabaseConnection(); } } /** * Try to create a new group * @param string $gid The name of the group to create * @return bool * * Tries to create a new group. If the group name already exists, false will * be returned. */ public function createGroup( $gid ) { $this->fixDI(); // Add group $result = $this->dbConn->insertIfNotExist('*PREFIX*groups', [ 'gid' => $gid, ]); // Add to cache $this->groupCache[$gid] = $gid; return $result === 1; } /** * delete a group * @param string $gid gid of the group to delete * @return bool * * Deletes a group and removes it from the group_user-table */ public function deleteGroup( $gid ) { $this->fixDI(); // Delete the group $qb = $this->dbConn->getQueryBuilder(); $qb->delete('groups') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); // Delete the group-user relation $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_user') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); // Delete the group-groupadmin relation $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_admin') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); // Delete from cache unset($this->groupCache[$gid]); return true; } /** * is user in group? * @param string $uid uid of the user * @param string $gid gid of the group * @return bool * * Checks whether the user is member of a group or not. */ public function inGroup( $uid, $gid ) { $this->fixDI(); // check $qb = $this->dbConn->getQueryBuilder(); $cursor = $qb->select('uid') ->from('group_user') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->execute(); $result = $cursor->fetch(); $cursor->closeCursor(); return $result ? true : false; } /** * Add a user to a group * @param string $uid Name of the user to add to group * @param string $gid Name of the group in which add the user * @return bool * * Adds a user to a group. */ public function addToGroup( $uid, $gid ) { $this->fixDI(); // No duplicate entries! if( !$this->inGroup( $uid, $gid )) { $qb = $this->dbConn->getQueryBuilder(); $qb->insert('group_user') ->setValue('uid', $qb->createNamedParameter($uid)) ->setValue('gid', $qb->createNamedParameter($gid)) ->execute(); return true; }else{ return false; } } /** * Removes a user from a group * @param string $uid Name of the user to remove from group * @param string $gid Name of the group from which remove the user * @return bool * * removes the user from a group. */ public function removeFromGroup( $uid, $gid ) { $this->fixDI(); $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_user') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->andWhere($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); return true; } /** * Get all groups a user belongs to * @param string $uid Name of the user * @return array an array of group names * * This function fetches all groups a user belongs to. It does not check * if the user exists at all. */ public function getUserGroups( $uid ) { //guests has empty or null $uid if ($uid === null || $uid === '') { return []; } $this->fixDI(); // No magic! $qb = $this->dbConn->getQueryBuilder(); $cursor = $qb->select('gid') ->from('group_user') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->execute(); $groups = []; while( $row = $cursor->fetch()) { $groups[] = $row["gid"]; $this->groupCache[$row['gid']] = $row['gid']; } $cursor->closeCursor(); return $groups; } /** * get a list of all groups * @param string $search * @param int $limit * @param int $offset * @return array an array of group names * * Returns a list with all groups */ public function getGroups($search = '', $limit = null, $offset = null) { $parameters = []; $searchLike = ''; if ($search !== '') { $parameters[] = '%' . $search . '%'; $searchLike = ' WHERE LOWER(`gid`) LIKE LOWER(?)'; } $stmt = \OC_DB::prepare('SELECT `gid` FROM `*PREFIX*groups`' . $searchLike . ' ORDER BY `gid` ASC', $limit, $offset); $result = $stmt->execute($parameters); $groups = array(); while ($row = $result->fetchRow()) { $groups[] = $row['gid']; } return $groups; } /** * check if a group exists * @param string $gid * @return bool */ public function groupExists($gid) { $this->fixDI(); // Check cache first if (isset($this->groupCache[$gid])) { return true; } $qb = $this->dbConn->getQueryBuilder(); $cursor = $qb->select('gid') ->from('groups') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); $result = $cursor->fetch(); $cursor->closeCursor(); if ($result !== false) { $this->groupCache[$gid] = $gid; return true; } return false; } /** * get a list of all users in a group * @param string $gid * @param string $search * @param int $limit * @param int $offset * @return array an array of user ids */ public function usersInGroup($gid, $search = '', $limit = null, $offset = null) { $parameters = [$gid]; $searchLike = ''; if ($search !== '') { $parameters[] = '%' . $this->dbConn->escapeLikeParameter($search) . '%'; $searchLike = ' AND `uid` LIKE ?'; } $stmt = \OC_DB::prepare('SELECT `uid` FROM `*PREFIX*group_user` WHERE `gid` = ?' . $searchLike . ' ORDER BY `uid` ASC', $limit, $offset); $result = $stmt->execute($parameters); $users = array(); while ($row = $result->fetchRow()) { $users[] = $row['uid']; } return $users; } /** * get the number of all users matching the search string in a group * @param string $gid * @param string $search * @return int|false * @throws \OC\DatabaseException */ public function countUsersInGroup($gid, $search = '') { $parameters = [$gid]; $searchLike = ''; if ($search !== '') { $parameters[] = '%' . $this->dbConn->escapeLikeParameter($search) . '%'; $searchLike = ' AND `uid` LIKE ?'; } $stmt = \OC_DB::prepare('SELECT COUNT(`uid`) AS `count` FROM `*PREFIX*group_user` WHERE `gid` = ?' . $searchLike); $result = $stmt->execute($parameters); $count = $result->fetchOne(); if($count !== false) { $count = intval($count); } return $count; } } private/Group/Manager.php 0000604 00000023432 15247130451 0011377 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author macjohnny <estebanmarin@gmx.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Roman Kreisel <mail@romankreisel.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author voxsim <Simon Vocella> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Group; use OC\Hooks\PublicEmitter; use OCP\GroupInterface; use OCP\IGroup; use OCP\IGroupManager; use OCP\ILogger; use OCP\IUser; /** * Class Manager * * Hooks available in scope \OC\Group: * - preAddUser(\OC\Group\Group $group, \OC\User\User $user) * - postAddUser(\OC\Group\Group $group, \OC\User\User $user) * - preRemoveUser(\OC\Group\Group $group, \OC\User\User $user) * - postRemoveUser(\OC\Group\Group $group, \OC\User\User $user) * - preDelete(\OC\Group\Group $group) * - postDelete(\OC\Group\Group $group) * - preCreate(string $groupId) * - postCreate(\OC\Group\Group $group) * * @package OC\Group */ class Manager extends PublicEmitter implements IGroupManager { /** * @var GroupInterface[] $backends */ private $backends = array(); /** * @var \OC\User\Manager $userManager */ private $userManager; /** * @var \OC\Group\Group[] */ private $cachedGroups = array(); /** * @var \OC\Group\Group[][] */ private $cachedUserGroups = array(); /** @var \OC\SubAdmin */ private $subAdmin = null; /** @var ILogger */ private $logger; /** * @param \OC\User\Manager $userManager * @param ILogger $logger */ public function __construct(\OC\User\Manager $userManager, ILogger $logger) { $this->userManager = $userManager; $this->logger = $logger; $cachedGroups = & $this->cachedGroups; $cachedUserGroups = & $this->cachedUserGroups; $this->listen('\OC\Group', 'postDelete', function ($group) use (&$cachedGroups, &$cachedUserGroups) { /** * @var \OC\Group\Group $group */ unset($cachedGroups[$group->getGID()]); $cachedUserGroups = array(); }); $this->listen('\OC\Group', 'postAddUser', function ($group) use (&$cachedUserGroups) { /** * @var \OC\Group\Group $group */ $cachedUserGroups = array(); }); $this->listen('\OC\Group', 'postRemoveUser', function ($group) use (&$cachedUserGroups) { /** * @var \OC\Group\Group $group */ $cachedUserGroups = array(); }); } /** * Checks whether a given backend is used * * @param string $backendClass Full classname including complete namespace * @return bool */ public function isBackendUsed($backendClass) { $backendClass = strtolower(ltrim($backendClass, '\\')); foreach ($this->backends as $backend) { if (strtolower(get_class($backend)) === $backendClass) { return true; } } return false; } /** * @param \OCP\GroupInterface $backend */ public function addBackend($backend) { $this->backends[] = $backend; $this->clearCaches(); } public function clearBackends() { $this->backends = array(); $this->clearCaches(); } protected function clearCaches() { $this->cachedGroups = array(); $this->cachedUserGroups = array(); } /** * @param string $gid * @return \OC\Group\Group */ public function get($gid) { if (isset($this->cachedGroups[$gid])) { return $this->cachedGroups[$gid]; } return $this->getGroupObject($gid); } /** * @param string $gid * @param string $displayName * @return \OCP\IGroup */ protected function getGroupObject($gid, $displayName = null) { $backends = array(); foreach ($this->backends as $backend) { if ($backend->implementsActions(\OC\Group\Backend::GROUP_DETAILS)) { $groupData = $backend->getGroupDetails($gid); if (is_array($groupData)) { // take the display name from the first backend that has a non-null one if (is_null($displayName) && isset($groupData['displayName'])) { $displayName = $groupData['displayName']; } $backends[] = $backend; } } else if ($backend->groupExists($gid)) { $backends[] = $backend; } } if (count($backends) === 0) { return null; } $this->cachedGroups[$gid] = new Group($gid, $backends, $this->userManager, $this, $displayName); return $this->cachedGroups[$gid]; } /** * @param string $gid * @return bool */ public function groupExists($gid) { return $this->get($gid) instanceof IGroup; } /** * @param string $gid * @return \OC\Group\Group */ public function createGroup($gid) { if ($gid === '' || $gid === null) { return false; } else if ($group = $this->get($gid)) { return $group; } else { $this->emit('\OC\Group', 'preCreate', array($gid)); foreach ($this->backends as $backend) { if ($backend->implementsActions(\OC\Group\Backend::CREATE_GROUP)) { $backend->createGroup($gid); $group = $this->getGroupObject($gid); $this->emit('\OC\Group', 'postCreate', array($group)); return $group; } } return null; } } /** * @param string $search * @param int $limit * @param int $offset * @return \OC\Group\Group[] */ public function search($search, $limit = null, $offset = null) { $groups = array(); foreach ($this->backends as $backend) { $groupIds = $backend->getGroups($search, $limit, $offset); foreach ($groupIds as $groupId) { $aGroup = $this->get($groupId); if ($aGroup instanceof IGroup) { $groups[$groupId] = $aGroup; } else { $this->logger->debug('Group "' . $groupId . '" was returned by search but not found through direct access', ['app' => 'core']); } } if (!is_null($limit) and $limit <= 0) { return array_values($groups); } } return array_values($groups); } /** * @param \OC\User\User|null $user * @return \OC\Group\Group[] */ public function getUserGroups($user) { if (!$user instanceof IUser) { return []; } return $this->getUserIdGroups($user->getUID()); } /** * @param string $uid the user id * @return \OC\Group\Group[] */ public function getUserIdGroups($uid) { if (isset($this->cachedUserGroups[$uid])) { return $this->cachedUserGroups[$uid]; } $groups = array(); foreach ($this->backends as $backend) { $groupIds = $backend->getUserGroups($uid); if (is_array($groupIds)) { foreach ($groupIds as $groupId) { $aGroup = $this->get($groupId); if ($aGroup instanceof IGroup) { $groups[$groupId] = $aGroup; } else { $this->logger->debug('User "' . $uid . '" belongs to deleted group: "' . $groupId . '"', ['app' => 'core']); } } } } $this->cachedUserGroups[$uid] = $groups; return $this->cachedUserGroups[$uid]; } /** * Checks if a userId is in the admin group * @param string $userId * @return bool if admin */ public function isAdmin($userId) { return $this->isInGroup($userId, 'admin'); } /** * Checks if a userId is in a group * @param string $userId * @param string $group * @return bool if in group */ public function isInGroup($userId, $group) { return array_key_exists($group, $this->getUserIdGroups($userId)); } /** * get a list of group ids for a user * @param \OC\User\User $user * @return array with group ids */ public function getUserGroupIds($user) { return array_map(function($value) { return (string) $value; }, array_keys($this->getUserGroups($user))); } /** * get a list of all display names in a group * @param string $gid * @param string $search * @param int $limit * @param int $offset * @return array an array of display names (value) and user ids (key) */ public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) { $group = $this->get($gid); if(is_null($group)) { return array(); } $search = trim($search); $groupUsers = array(); if(!empty($search)) { // only user backends have the capability to do a complex search for users $searchOffset = 0; $searchLimit = $limit * 100; if($limit === -1) { $searchLimit = 500; } do { $filteredUsers = $this->userManager->searchDisplayName($search, $searchLimit, $searchOffset); foreach($filteredUsers as $filteredUser) { if($group->inGroup($filteredUser)) { $groupUsers[]= $filteredUser; } } $searchOffset += $searchLimit; } while(count($groupUsers) < $searchLimit+$offset && count($filteredUsers) >= $searchLimit); if($limit === -1) { $groupUsers = array_slice($groupUsers, $offset); } else { $groupUsers = array_slice($groupUsers, $offset, $limit); } } else { $groupUsers = $group->searchUsers('', $limit, $offset); } $matchingUsers = array(); foreach($groupUsers as $groupUser) { $matchingUsers[$groupUser->getUID()] = $groupUser->getDisplayName(); } return $matchingUsers; } /** * @return \OC\SubAdmin */ public function getSubAdmin() { if (!$this->subAdmin) { $this->subAdmin = new \OC\SubAdmin( $this->userManager, $this, \OC::$server->getDatabaseConnection() ); } return $this->subAdmin; } } private/Group/MetaData.php 0000604 00000013354 15247130451 0011507 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Stephan Peijnik <speijnik@anexia-it.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Group; use OCP\IUserSession; class MetaData { const SORT_NONE = 0; const SORT_USERCOUNT = 1; // May have performance issues on LDAP backends const SORT_GROUPNAME = 2; /** @var string */ protected $user; /** @var bool */ protected $isAdmin; /** @var array */ protected $metaData = array(); /** @var \OCP\IGroupManager */ protected $groupManager; /** @var bool */ protected $sorting = false; /** @var IUserSession */ protected $userSession; /** * @param string $user the uid of the current user * @param bool $isAdmin whether the current users is an admin * @param \OCP\IGroupManager $groupManager * @param IUserSession $userSession */ public function __construct( $user, $isAdmin, \OCP\IGroupManager $groupManager, IUserSession $userSession ) { $this->user = $user; $this->isAdmin = (bool)$isAdmin; $this->groupManager = $groupManager; $this->userSession = $userSession; } /** * returns an array with meta data about all available groups * the array is structured as follows: * [0] array containing meta data about admin groups * [1] array containing meta data about unprivileged groups * @param string $groupSearch only effective when instance was created with * isAdmin being true * @param string $userSearch the pattern users are search for * @return array */ public function get($groupSearch = '', $userSearch = '') { $key = $groupSearch . '::' . $userSearch; if(isset($this->metaData[$key])) { return $this->metaData[$key]; } $adminGroups = array(); $groups = array(); $sortGroupsIndex = 0; $sortGroupsKeys = array(); $sortAdminGroupsIndex = 0; $sortAdminGroupsKeys = array(); foreach($this->getGroups($groupSearch) as $group) { $groupMetaData = $this->generateGroupMetaData($group, $userSearch); if (strtolower($group->getGID()) !== 'admin') { $this->addEntry( $groups, $sortGroupsKeys, $sortGroupsIndex, $groupMetaData); } else { //admin group is hard coded to 'admin' for now. In future, //backends may define admin groups too. Then the if statement //has to be adjusted accordingly. $this->addEntry( $adminGroups, $sortAdminGroupsKeys, $sortAdminGroupsIndex, $groupMetaData); } } //whether sorting is necessary is will be checked in sort() $this->sort($groups, $sortGroupsKeys); $this->sort($adminGroups, $sortAdminGroupsKeys); $this->metaData[$key] = array($adminGroups, $groups); return $this->metaData[$key]; } /** * sets the sort mode, see SORT_* constants for supported modes * * @param int $sortMode */ public function setSorting($sortMode) { switch ($sortMode) { case self::SORT_USERCOUNT: case self::SORT_GROUPNAME: $this->sorting = $sortMode; break; default: $this->sorting = self::SORT_NONE; } } /** * adds an group entry to the resulting array * @param array $entries the resulting array, by reference * @param array $sortKeys the sort key array, by reference * @param int $sortIndex the sort key index, by reference * @param array $data the group's meta data as returned by generateGroupMetaData() */ private function addEntry(&$entries, &$sortKeys, &$sortIndex, $data) { $entries[] = $data; if ($this->sorting === self::SORT_USERCOUNT) { $sortKeys[$sortIndex] = $data['usercount']; $sortIndex++; } else if ($this->sorting === self::SORT_GROUPNAME) { $sortKeys[$sortIndex] = $data['name']; $sortIndex++; } } /** * creates an array containing the group meta data * @param \OCP\IGroup $group * @param string $userSearch * @return array with the keys 'id', 'name' and 'usercount' */ private function generateGroupMetaData(\OCP\IGroup $group, $userSearch) { return array( 'id' => $group->getGID(), 'name' => $group->getGID(), 'usercount' => $this->sorting === self::SORT_USERCOUNT ? $group->count($userSearch) : 0, ); } /** * sorts the result array, if applicable * @param array $entries the result array, by reference * @param array $sortKeys the array containing the sort keys * @param return null */ private function sort(&$entries, $sortKeys) { if ($this->sorting === self::SORT_USERCOUNT) { array_multisort($sortKeys, SORT_DESC, $entries); } else if ($this->sorting === self::SORT_GROUPNAME) { array_multisort($sortKeys, SORT_ASC, $entries); } } /** * returns the available groups * @param string $search a search string * @return \OCP\IGroup[] */ protected function getGroups($search = '') { if($this->isAdmin) { return $this->groupManager->search($search); } else { $userObject = $this->userSession->getUser(); if($userObject !== null) { $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($userObject); } else { $groups = []; } return $groups; } } } private/Group/Group.php 0000604 00000016076 15247130451 0011127 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Group; use OCP\IGroup; class Group implements IGroup { /** @var null|string */ protected $displayName; /** * @var string $id */ private $gid; /** * @var \OC\User\User[] $users */ private $users = array(); /** * @var bool $usersLoaded */ private $usersLoaded; /** * @var \OC\Group\Backend[]|\OC\Group\Database[] $backend */ private $backends; /** * @var \OC\Hooks\PublicEmitter $emitter */ private $emitter; /** * @var \OC\User\Manager $userManager */ private $userManager; /** * @param string $gid * @param \OC\Group\Backend[] $backends * @param \OC\User\Manager $userManager * @param \OC\Hooks\PublicEmitter $emitter * @param string $displayName */ public function __construct($gid, $backends, $userManager, $emitter = null, $displayName = null) { $this->gid = $gid; $this->backends = $backends; $this->userManager = $userManager; $this->emitter = $emitter; $this->displayName = $displayName; } public function getGID() { return $this->gid; } public function getDisplayName() { if (is_null($this->displayName)) { return $this->gid; } return $this->displayName; } /** * get all users in the group * * @return \OC\User\User[] */ public function getUsers() { if ($this->usersLoaded) { return $this->users; } $userIds = array(); foreach ($this->backends as $backend) { $diff = array_diff( $backend->usersInGroup($this->gid), $userIds ); if ($diff) { $userIds = array_merge($userIds, $diff); } } $this->users = $this->getVerifiedUsers($userIds); $this->usersLoaded = true; return $this->users; } /** * check if a user is in the group * * @param \OC\User\User $user * @return bool */ public function inGroup($user) { if (isset($this->users[$user->getUID()])) { return true; } foreach ($this->backends as $backend) { if ($backend->inGroup($user->getUID(), $this->gid)) { $this->users[$user->getUID()] = $user; return true; } } return false; } /** * add a user to the group * * @param \OC\User\User $user */ public function addUser($user) { if ($this->inGroup($user)) { return; } if ($this->emitter) { $this->emitter->emit('\OC\Group', 'preAddUser', array($this, $user)); } foreach ($this->backends as $backend) { if ($backend->implementsActions(\OC\Group\Backend::ADD_TO_GROUP)) { $backend->addToGroup($user->getUID(), $this->gid); if ($this->users) { $this->users[$user->getUID()] = $user; } if ($this->emitter) { $this->emitter->emit('\OC\Group', 'postAddUser', array($this, $user)); } return; } } } /** * remove a user from the group * * @param \OC\User\User $user */ public function removeUser($user) { $result = false; if ($this->emitter) { $this->emitter->emit('\OC\Group', 'preRemoveUser', array($this, $user)); } foreach ($this->backends as $backend) { if ($backend->implementsActions(\OC\Group\Backend::REMOVE_FROM_GOUP) and $backend->inGroup($user->getUID(), $this->gid)) { $backend->removeFromGroup($user->getUID(), $this->gid); $result = true; } } if ($result) { if ($this->emitter) { $this->emitter->emit('\OC\Group', 'postRemoveUser', array($this, $user)); } if ($this->users) { foreach ($this->users as $index => $groupUser) { if ($groupUser->getUID() === $user->getUID()) { unset($this->users[$index]); return; } } } } } /** * search for users in the group by userid * * @param string $search * @param int $limit * @param int $offset * @return \OC\User\User[] */ public function searchUsers($search, $limit = null, $offset = null) { $users = array(); foreach ($this->backends as $backend) { $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); $users += $this->getVerifiedUsers($userIds); if (!is_null($limit) and $limit <= 0) { return array_values($users); } } return array_values($users); } /** * returns the number of users matching the search string * * @param string $search * @return int|bool */ public function count($search = '') { $users = false; foreach ($this->backends as $backend) { if($backend->implementsActions(\OC\Group\Backend::COUNT_USERS)) { if($users === false) { //we could directly add to a bool variable, but this would //be ugly $users = 0; } $users += $backend->countUsersInGroup($this->gid, $search); } } return $users; } /** * search for users in the group by displayname * * @param string $search * @param int $limit * @param int $offset * @return \OC\User\User[] */ public function searchDisplayName($search, $limit = null, $offset = null) { $users = array(); foreach ($this->backends as $backend) { $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); $users = $this->getVerifiedUsers($userIds); if (!is_null($limit) and $limit <= 0) { return array_values($users); } } return array_values($users); } /** * delete the group * * @return bool */ public function delete() { // Prevent users from deleting group admin if ($this->getGID() === 'admin') { return false; } $result = false; if ($this->emitter) { $this->emitter->emit('\OC\Group', 'preDelete', array($this)); } foreach ($this->backends as $backend) { if ($backend->implementsActions(\OC\Group\Backend::DELETE_GROUP)) { $result = true; $backend->deleteGroup($this->gid); } } if ($result and $this->emitter) { $this->emitter->emit('\OC\Group', 'postDelete', array($this)); } return $result; } /** * returns all the Users from an array that really exists * @param string[] $userIds an array containing user IDs * @return \OC\User\User[] an Array with the userId as Key and \OC\User\User as value */ private function getVerifiedUsers($userIds) { if (!is_array($userIds)) { return array(); } $users = array(); foreach ($userIds as $userId) { $user = $this->userManager->get($userId); if (!is_null($user)) { $users[$userId] = $user; } } return $users; } } private/Cache/File.php 0000604 00000012711 15247130451 0010611 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Cache; use OC\Files\Filesystem; use OC\Files\View; use OCP\ICache; use OCP\Security\ISecureRandom; class File implements ICache { /** @var View */ protected $storage; /** * Returns the cache storage for the logged in user * * @return \OC\Files\View cache storage * @throws \OC\ForbiddenException * @throws \OC\User\NoUserException */ protected function getStorage() { if (isset($this->storage)) { return $this->storage; } if (\OC::$server->getUserSession()->isLoggedIn()) { $rootView = new View(); $user = \OC::$server->getUserSession()->getUser(); Filesystem::initMountPoints($user->getUID()); if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) { $rootView->mkdir('/' . $user->getUID() . '/cache'); } $this->storage = new View('/' . $user->getUID() . '/cache'); return $this->storage; } else { \OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', \OCP\Util::ERROR); throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in'); } } /** * @param string $key * @return mixed|null * @throws \OC\ForbiddenException */ public function get($key) { $result = null; if ($this->hasKey($key)) { $storage = $this->getStorage(); $result = $storage->file_get_contents($key); } return $result; } /** * Returns the size of the stored/cached data * * @param string $key * @return int */ public function size($key) { $result = 0; if ($this->hasKey($key)) { $storage = $this->getStorage(); $result = $storage->filesize($key); } return $result; } /** * @param string $key * @param mixed $value * @param int $ttl * @return bool|mixed * @throws \OC\ForbiddenException */ public function set($key, $value, $ttl = 0) { $storage = $this->getStorage(); $result = false; // unique id to avoid chunk collision, just in case $uniqueId = \OC::$server->getSecureRandom()->generate( 16, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER ); // use part file to prevent hasKey() to find the key // while it is being written $keyPart = $key . '.' . $uniqueId . '.part'; if ($storage and $storage->file_put_contents($keyPart, $value)) { if ($ttl === 0) { $ttl = 86400; // 60*60*24 } $result = $storage->touch($keyPart, time() + $ttl); $result &= $storage->rename($keyPart, $key); } return $result; } /** * @param string $key * @return bool * @throws \OC\ForbiddenException */ public function hasKey($key) { $storage = $this->getStorage(); if ($storage && $storage->is_file($key) && $storage->isReadable($key)) { return true; } return false; } /** * @param string $key * @return bool|mixed * @throws \OC\ForbiddenException */ public function remove($key) { $storage = $this->getStorage(); if (!$storage) { return false; } return $storage->unlink($key); } /** * @param string $prefix * @return bool * @throws \OC\ForbiddenException */ public function clear($prefix = '') { $storage = $this->getStorage(); if ($storage and $storage->is_dir('/')) { $dh = $storage->opendir('/'); if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) { $storage->unlink('/' . $file); } } } } return true; } /** * Runs GC * @throws \OC\ForbiddenException */ public function gc() { $storage = $this->getStorage(); if ($storage and $storage->is_dir('/')) { // extra hour safety, in case of stray part chunks that take longer to write, // because touch() is only called after the chunk was finished $now = time() - 3600; $dh = $storage->opendir('/'); if (!is_resource($dh)) { return null; } while (($file = readdir($dh)) !== false) { if ($file != '.' and $file != '..') { try { $mtime = $storage->filemtime('/' . $file); if ($mtime < $now) { $storage->unlink('/' . $file); } } catch (\OCP\Lock\LockedException $e) { // ignore locked chunks \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core')); } catch (\OCP\Files\ForbiddenException $e) { \OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', array('app' => 'core')); } catch (\OCP\Files\LockNotAcquiredException $e) { \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core')); } } } } } } private/Cache/CappedMemoryCache.php 0000604 00000004127 15247130451 0013245 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Cache; use OCP\ICache; /** * In-memory cache with a capacity limit to keep memory usage in check * * Uses a simple FIFO expiry mechanism */ class CappedMemoryCache implements ICache, \ArrayAccess { private $capacity; private $cache = []; public function __construct($capacity = 512) { $this->capacity = $capacity; } public function hasKey($key) { return isset($this->cache[$key]); } public function get($key) { return isset($this->cache[$key]) ? $this->cache[$key] : null; } public function set($key, $value, $ttl = 0) { if (is_null($key)) { $this->cache[] = $value; } else { $this->cache[$key] = $value; } $this->garbageCollect(); } public function remove($key) { unset($this->cache[$key]); return true; } public function clear($prefix = '') { $this->cache = []; return true; } public function offsetExists($offset) { return $this->hasKey($offset); } public function &offsetGet($offset) { return $this->cache[$offset]; } public function offsetSet($offset, $value) { $this->set($offset, $value); } public function offsetUnset($offset) { $this->remove($offset); } public function getData() { return $this->cache; } private function garbageCollect() { while (count($this->cache) > $this->capacity) { reset($this->cache); $key = key($this->cache); $this->remove($key); } } } private/SubAdmin.php 0000604 00000016053 15247130451 0010434 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Georg Ehrke <georg@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\Hooks\PublicEmitter; use OCP\IUser; use OCP\IUserManager; use OCP\IGroup; use OCP\IGroupManager; use OCP\IDBConnection; class SubAdmin extends PublicEmitter { /** @var IUserManager */ private $userManager; /** @var IGroupManager */ private $groupManager; /** @var IDBConnection */ private $dbConn; /** * @param IUserManager $userManager * @param IGroupManager $groupManager * @param IDBConnection $dbConn */ public function __construct(IUserManager $userManager, IGroupManager $groupManager, IDBConnection $dbConn) { $this->userManager = $userManager; $this->groupManager = $groupManager; $this->dbConn = $dbConn; $this->userManager->listen('\OC\User', 'postDelete', function($user) { $this->post_deleteUser($user); }); $this->groupManager->listen('\OC\Group', 'postDelete', function($group) { $this->post_deleteGroup($group); }); } /** * add a SubAdmin * @param IUser $user user to be SubAdmin * @param IGroup $group group $user becomes subadmin of * @return bool */ public function createSubAdmin(IUser $user, IGroup $group) { $qb = $this->dbConn->getQueryBuilder(); $qb->insert('group_admin') ->values([ 'gid' => $qb->createNamedParameter($group->getGID()), 'uid' => $qb->createNamedParameter($user->getUID()) ]) ->execute(); $this->emit('\OC\SubAdmin', 'postCreateSubAdmin', [$user, $group]); \OC_Hook::emit("OC_SubAdmin", "post_createSubAdmin", ["gid" => $group->getGID()]); return true; } /** * delete a SubAdmin * @param IUser $user the user that is the SubAdmin * @param IGroup $group the group * @return bool */ public function deleteSubAdmin(IUser $user, IGroup $group) { $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_admin') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($group->getGID()))) ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) ->execute(); $this->emit('\OC\SubAdmin', 'postDeleteSubAdmin', [$user, $group]); \OC_Hook::emit("OC_SubAdmin", "post_deleteSubAdmin", ["gid" => $group->getGID()]); return true; } /** * get groups of a SubAdmin * @param IUser $user the SubAdmin * @return IGroup[] */ public function getSubAdminsGroups(IUser $user) { $qb = $this->dbConn->getQueryBuilder(); $result = $qb->select('gid') ->from('group_admin') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) ->execute(); $groups = []; while($row = $result->fetch()) { $group = $this->groupManager->get($row['gid']); if(!is_null($group)) { $groups[] = $group; } } $result->closeCursor(); return $groups; } /** * get SubAdmins of a group * @param IGroup $group the group * @return IUser[] */ public function getGroupsSubAdmins(IGroup $group) { $qb = $this->dbConn->getQueryBuilder(); $result = $qb->select('uid') ->from('group_admin') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($group->getGID()))) ->execute(); $users = []; while($row = $result->fetch()) { $user = $this->userManager->get($row['uid']); if(!is_null($user)) { $users[] = $user; } } $result->closeCursor(); return $users; } /** * get all SubAdmins * @return array */ public function getAllSubAdmins() { $qb = $this->dbConn->getQueryBuilder(); $result = $qb->select('*') ->from('group_admin') ->execute(); $subadmins = []; while($row = $result->fetch()) { $user = $this->userManager->get($row['uid']); $group = $this->groupManager->get($row['gid']); if(!is_null($user) && !is_null($group)) { $subadmins[] = [ 'user' => $user, 'group' => $group ]; } } $result->closeCursor(); return $subadmins; } /** * checks if a user is a SubAdmin of a group * @param IUser $user * @param IGroup $group * @return bool */ public function isSubAdminOfGroup(IUser $user, IGroup $group) { $qb = $this->dbConn->getQueryBuilder(); /* * Primary key is ('gid', 'uid') so max 1 result possible here */ $result = $qb->select('*') ->from('group_admin') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($group->getGID()))) ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) ->execute(); $fetch = $result->fetch(); $result->closeCursor(); $result = !empty($fetch) ? true : false; return $result; } /** * checks if a user is a SubAdmin * @param IUser $user * @return bool */ public function isSubAdmin(IUser $user) { // Check if the user is already an admin if ($this->groupManager->isAdmin($user->getUID())) { return true; } $qb = $this->dbConn->getQueryBuilder(); $result = $qb->select('gid') ->from('group_admin') ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) ->setMaxResults(1) ->execute(); $isSubAdmin = $result->fetch(); $result->closeCursor(); $result = $isSubAdmin === false ? false : true; return $result; } /** * checks if a user is a accessible by a subadmin * @param IUser $subadmin * @param IUser $user * @return bool */ public function isUserAccessible($subadmin, $user) { if(!$this->isSubAdmin($subadmin)) { return false; } if($this->groupManager->isAdmin($user->getUID())) { return false; } $accessibleGroups = $this->getSubAdminsGroups($subadmin); foreach($accessibleGroups as $accessibleGroup) { if($accessibleGroup->inGroup($user)) { return true; } } return false; } /** * delete all SubAdmins by $user * @param IUser $user * @return boolean */ private function post_deleteUser($user) { $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_admin') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) ->execute(); return true; } /** * delete all SubAdmins by $group * @param IGroup $group * @return boolean */ private function post_deleteGroup($group) { $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_admin') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($group->getGID()))) ->execute(); return true; } } private/TemplateLayout.php 0000604 00000021075 15247130451 0011703 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Clark Tomlinson <fallen013@gmail.com> * @author Hendrik Leppelsack <hendrik@leppelsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Remco Brenninkmeijer <requist1@starmail.nl> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\Template\JSCombiner; use OC\Template\JSConfigHelper; use OC\Template\SCSSCacher; use OCP\Defaults; class TemplateLayout extends \OC_Template { private static $versionHash = ''; /** * @var \OCP\IConfig */ private $config; /** * @param string $renderAs * @param string $appId application id */ public function __construct( $renderAs, $appId = '' ) { // yes - should be injected .... $this->config = \OC::$server->getConfig(); // Decide which page we show if($renderAs == 'user') { parent::__construct( 'core', 'layout.user' ); if(in_array(\OC_App::getCurrentApp(), ['settings','admin', 'help']) !== false) { $this->assign('bodyid', 'body-settings'); }else{ $this->assign('bodyid', 'body-user'); } // Code integrity notification $integrityChecker = \OC::$server->getIntegrityCodeChecker(); if(\OC_User::isAdminUser(\OC_User::getUser()) && $integrityChecker->isCodeCheckEnforced() && !$integrityChecker->hasPassedCheck()) { \OCP\Util::addScript('core', 'integritycheck-failed-notification'); } // Add navigation entry $this->assign( 'application', ''); $this->assign( 'appid', $appId ); $navigation = \OC_App::getNavigation(); $this->assign( 'navigation', $navigation); $settingsNavigation = \OC_App::getSettingsNavigation(); $this->assign( 'settingsnavigation', $settingsNavigation); foreach($navigation as $entry) { if ($entry['active']) { $this->assign( 'application', $entry['name'] ); break; } } foreach($settingsNavigation as $entry) { if ($entry['active']) { $this->assign( 'application', $entry['name'] ); break; } } $userDisplayName = \OC_User::getDisplayName(); $this->assign('user_displayname', $userDisplayName); $this->assign('user_uid', \OC_User::getUser()); if (\OC_User::getUser() === false) { $this->assign('userAvatarSet', false); } else { $this->assign('userAvatarSet', \OC::$server->getAvatarManager()->getAvatar(\OC_User::getUser())->exists()); $this->assign('userAvatarVersion', \OC::$server->getConfig()->getUserValue(\OC_User::getUser(), 'avatar', 'version', 0)); } } else if ($renderAs == 'error') { parent::__construct('core', 'layout.guest', '', false); $this->assign('bodyid', 'body-login'); } else if ($renderAs == 'guest') { parent::__construct('core', 'layout.guest'); $this->assign('bodyid', 'body-login'); } else { parent::__construct('core', 'layout.base'); } // Send the language to our layouts $this->assign('language', \OC::$server->getL10NFactory()->findLanguage()); if(\OC::$server->getSystemConfig()->getValue('installed', false)) { if (empty(self::$versionHash)) { $v = \OC_App::getAppVersions(); $v['core'] = implode('.', \OCP\Util::getVersion()); self::$versionHash = md5(implode(',', $v)); } } else { self::$versionHash = md5('not installed'); } // Add the js files $jsFiles = self::findJavascriptFiles(\OC_Util::$scripts); $this->assign('jsfiles', array()); if ($this->config->getSystemValue('installed', false) && $renderAs != 'error') { if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) { $jsConfigHelper = new JSConfigHelper( \OC::$server->getL10N('core'), \OC::$server->query(Defaults::class), \OC::$server->getAppManager(), \OC::$server->getSession(), \OC::$server->getUserSession()->getUser(), \OC::$server->getConfig(), \OC::$server->getGroupManager(), \OC::$server->getIniWrapper(), \OC::$server->getURLGenerator() ); $this->assign('inline_ocjs', $jsConfigHelper->getConfig()); } else { $this->append('jsfiles', \OC::$server->getURLGenerator()->linkToRoute('core.OCJS.getConfig', ['v' => self::$versionHash])); } } foreach($jsFiles as $info) { $web = $info[1]; $file = $info[2]; $this->append( 'jsfiles', $web.'/'.$file . $this->getVersionHashSuffix() ); } try { $pathInfo = \OC::$server->getRequest()->getPathInfo(); } catch (\Exception $e) { $pathInfo = ''; } // Do not initialise scss appdata until we have a fully installed instance // Do not load scss for update, errors, installation or login page if(\OC::$server->getSystemConfig()->getValue('installed', false) && !\OCP\Util::needUpgrade() && $pathInfo !== '' && !preg_match('/^\/login/', $pathInfo)) { $cssFiles = self::findStylesheetFiles(\OC_Util::$styles); } else { // If we ignore the scss compiler, // we need to load the guest css fallback \OC_Util::addStyle('guest'); $cssFiles = self::findStylesheetFiles(\OC_Util::$styles, false); } $this->assign('cssfiles', array()); $this->assign('printcssfiles', []); $this->assign('versionHash', self::$versionHash); foreach($cssFiles as $info) { $web = $info[1]; $file = $info[2]; if (substr($file, -strlen('print.css')) === 'print.css') { $this->append( 'printcssfiles', $web.'/'.$file . $this->getVersionHashSuffix() ); } else { $this->append( 'cssfiles', $web.'/'.$file . $this->getVersionHashSuffix() ); } } } protected function getVersionHashSuffix() { if(\OC::$server->getConfig()->getSystemValue('debug', false)) { // allows chrome workspace mapping in debug mode return ""; } if ($this->config->getSystemValue('installed', false) && \OC::$server->getAppManager()->isInstalled('theming')) { return '?v=' . self::$versionHash . '-' . $this->config->getAppValue('theming', 'cachebuster', '0'); } return '?v=' . self::$versionHash; } /** * @param array $styles * @return array */ static public function findStylesheetFiles($styles, $compileScss = true) { // Read the selected theme from the config file $theme = \OC_Util::getTheme(); if($compileScss) { $SCSSCacher = \OC::$server->query(SCSSCacher::class); } else { $SCSSCacher = null; } $locator = new \OC\Template\CSSResourceLocator( \OC::$server->getLogger(), $theme, array( \OC::$SERVERROOT => \OC::$WEBROOT ), array( \OC::$SERVERROOT => \OC::$WEBROOT ), $SCSSCacher ); $locator->find($styles); return $locator->getResources(); } /** * @param array $scripts * @return array */ static public function findJavascriptFiles($scripts) { // Read the selected theme from the config file $theme = \OC_Util::getTheme(); $locator = new \OC\Template\JSResourceLocator( \OC::$server->getLogger(), $theme, array( \OC::$SERVERROOT => \OC::$WEBROOT ), array( \OC::$SERVERROOT => \OC::$WEBROOT ), new JSCombiner( \OC::$server->getAppDataDir('js'), \OC::$server->getURLGenerator(), \OC::$server->getMemCacheFactory()->create('JS'), \OC::$server->getSystemConfig() ) ); $locator->find($scripts); return $locator->getResources(); } /** * Converts the absolute file path to a relative path from \OC::$SERVERROOT * @param string $filePath Absolute path * @return string Relative path * @throws \Exception If $filePath is not under \OC::$SERVERROOT */ public static function convertToRelativePath($filePath) { $relativePath = explode(\OC::$SERVERROOT, $filePath); if(count($relativePath) !== 2) { throw new \Exception('$filePath is not under the \OC::$SERVERROOT'); } return $relativePath[1]; } } private/HTTPHelper.php 0000604 00000006171 15247130451 0010651 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OCP\Http\Client\IClientService; use OCP\IConfig; /** * Class HTTPHelper * * @package OC * @deprecated Use \OCP\Http\Client\IClientService */ class HTTPHelper { const USER_AGENT = 'ownCloud Server Crawler'; /** @var \OCP\IConfig */ private $config; /** @var IClientService */ private $clientService; /** * @param IConfig $config * @param IClientService $clientService */ public function __construct(IConfig $config, IClientService $clientService) { $this->config = $config; $this->clientService = $clientService; } /** * Get URL content * @param string $url Url to get content * @throws \Exception If the URL does not start with http:// or https:// * @return string of the response or false on error * This function get the content of a page via curl, if curl is enabled. * If not, file_get_contents is used. * @deprecated Use \OCP\Http\Client\IClientService */ public function getUrlContent($url) { try { $client = $this->clientService->newClient(); $response = $client->get($url); return $response->getBody(); } catch (\Exception $e) { return false; } } /** * Returns the response headers of a HTTP URL without following redirects * @param string $location Needs to be a HTTPS or HTTP URL * @return array * @deprecated Use \OCP\Http\Client\IClientService */ public function getHeaders($location) { $client = $this->clientService->newClient(); $response = $client->get($location); return $response->getHeaders(); } /** * Checks whether the supplied URL begins with HTTPS:// or HTTP:// (case insensitive) * @param string $url * @return bool */ public function isHTTPURL($url) { return stripos($url, 'https://') === 0 || stripos($url, 'http://') === 0; } /** * send http post request * * @param string $url * @param array $fields data send by the request * @return array * @deprecated Use \OCP\Http\Client\IClientService */ public function post($url, array $fields) { $client = $this->clientService->newClient(); try { $response = $client->post( $url, [ 'body' => $fields, 'connect_timeout' => 10, ] ); } catch (\Exception $e) { return ['success' => false, 'result' => $e->getMessage()]; } return ['success' => true, 'result' => $response->getBody()]; } } private/ServerNotAvailableException.php 0000604 00000001473 15247130451 0014341 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class ServerNotAvailableException extends \Exception { } private/Installer.php 0000604 00000043154 15247130451 0010671 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Brice Maron <brice@bmaron.net> * @author Christian Weiske <cweiske@cweiske.de> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Kamil Domanski <kdomanski@kdemail.net> * @author Lukas Reschke <lukas@statuscode.ch> * @author michag86 <micha_g@arcor.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author root <root@oc.(none)> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use Doctrine\DBAL\Exception\TableExistsException; use OC\App\AppManager; use OC\App\AppStore\Bundles\Bundle; use OC\App\AppStore\Fetcher\AppFetcher; use OC\App\CodeChecker\CodeChecker; use OC\App\CodeChecker\EmptyCheck; use OC\App\CodeChecker\PrivateCheck; use OC\Archive\TAR; use OC_App; use OC_DB; use OC_Helper; use OCP\App\IAppManager; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\ILogger; use OCP\ITempManager; use phpseclib\File\X509; /** * This class provides the functionality needed to install, update and remove apps */ class Installer { /** @var AppFetcher */ private $appFetcher; /** @var IClientService */ private $clientService; /** @var ITempManager */ private $tempManager; /** @var ILogger */ private $logger; /** @var IConfig */ private $config; /** * @param AppFetcher $appFetcher * @param IClientService $clientService * @param ITempManager $tempManager * @param ILogger $logger * @param IConfig $config */ public function __construct(AppFetcher $appFetcher, IClientService $clientService, ITempManager $tempManager, ILogger $logger, IConfig $config) { $this->appFetcher = $appFetcher; $this->clientService = $clientService; $this->tempManager = $tempManager; $this->logger = $logger; $this->config = $config; } /** * Installs an app that is located in one of the app folders already * * @param string $appId App to install * @throws \Exception * @return string app ID */ public function installApp($appId) { $app = \OC_App::findAppInDirectories($appId); if($app === false) { throw new \Exception('App not found in any app directory'); } $basedir = $app['path'].'/'.$appId; $info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true); $l = \OC::$server->getL10N('core'); if(!is_array($info)) { throw new \Exception( $l->t('App "%s" cannot be installed because appinfo file cannot be read.', [$info['name']] ) ); } $version = \OCP\Util::getVersion(); if (!\OC_App::isAppCompatible($version, $info)) { throw new \Exception( // TODO $l $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.', [$info['name']] ) ); } // check for required dependencies \OC_App::checkAppDependencies($this->config, $l, $info); //install the database if(is_file($basedir.'/appinfo/database.xml')) { if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) { OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml'); } else { OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml'); } } \OC_App::registerAutoloading($appId, $basedir); \OC_App::setupBackgroundJobs($info['background-jobs']); if(isset($info['settings']) && is_array($info['settings'])) { \OC::$server->getSettingsManager()->setupSettings($info['settings']); } //run appinfo/install.php if((!isset($data['noinstall']) or $data['noinstall']==false)) { self::includeAppScript($basedir . '/appinfo/install.php'); } $appData = OC_App::getAppInfo($appId); OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']); //set the installed version \OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false)); \OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no'); //set remote/public handlers foreach($info['remote'] as $name=>$path) { \OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path); } foreach($info['public'] as $name=>$path) { \OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path); } OC_App::setAppTypes($info['id']); return $info['id']; } /** * @brief checks whether or not an app is installed * @param string $app app * @returns bool * * Checks whether or not an app is installed, i.e. registered in apps table. */ public static function isInstalled( $app ) { return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null); } /** * Updates the specified app from the appstore * * @param string $appId * @return bool */ public function updateAppstoreApp($appId) { if(self::isUpdateAvailable($appId, $this->appFetcher)) { try { $this->downloadApp($appId); } catch (\Exception $e) { $this->logger->error($e->getMessage(), ['app' => 'core']); return false; } return OC_App::updateApp($appId); } return false; } /** * Downloads an app and puts it into the app directory * * @param string $appId * * @throws \Exception If the installation was not successful */ public function downloadApp($appId) { $appId = strtolower($appId); $apps = $this->appFetcher->get(); foreach($apps as $app) { if($app['id'] === $appId) { // Load the certificate $certificate = new X509(); $certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt')); $loadedCertificate = $certificate->loadX509($app['certificate']); // Verify if the certificate has been revoked $crl = new X509(); $crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt')); $crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl')); if($crl->validateSignature() !== true) { throw new \Exception('Could not validate CRL signature'); } $csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString(); $revoked = $crl->getRevoked($csn); if ($revoked !== false) { throw new \Exception( sprintf( 'Certificate "%s" has been revoked', $csn ) ); } // Verify if the certificate has been issued by the Nextcloud Code Authority CA if($certificate->validateSignature() !== true) { throw new \Exception( sprintf( 'App with id %s has a certificate not issued by a trusted Code Signing Authority', $appId ) ); } // Verify if the certificate is issued for the requested app id $certInfo = openssl_x509_parse($app['certificate']); if(!isset($certInfo['subject']['CN'])) { throw new \Exception( sprintf( 'App with id %s has a cert with no CN', $appId ) ); } if($certInfo['subject']['CN'] !== $appId) { throw new \Exception( sprintf( 'App with id %s has a cert issued to %s', $appId, $certInfo['subject']['CN'] ) ); } // Download the release $tempFile = $this->tempManager->getTemporaryFile('.tar.gz'); $client = $this->clientService->newClient(); $client->get($app['releases'][0]['download'], ['save_to' => $tempFile]); // Check if the signature actually matches the downloaded content $certificate = openssl_get_publickey($app['certificate']); $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512); openssl_free_key($certificate); if($verified === true) { // Seems to match, let's proceed $extractDir = $this->tempManager->getTemporaryFolder(); $archive = new TAR($tempFile); if($archive) { $archive->extract($extractDir); $allFiles = scandir($extractDir); $folders = array_diff($allFiles, ['.', '..']); $folders = array_values($folders); if(count($folders) > 1) { throw new \Exception( sprintf( 'Extracted app %s has more than 1 folder', $appId ) ); } // Check if appinfo/info.xml has the same app ID as well $loadEntities = libxml_disable_entity_loader(false); $xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml'); libxml_disable_entity_loader($loadEntities); if((string)$xml->id !== $appId) { throw new \Exception( sprintf( 'App for id %s has a wrong app ID in info.xml: %s', $appId, (string)$xml->id ) ); } // Check if the version is lower than before $currentVersion = OC_App::getAppVersion($appId); $newVersion = (string)$xml->version; if(version_compare($currentVersion, $newVersion) === 1) { throw new \Exception( sprintf( 'App for id %s has version %s and tried to update to lower version %s', $appId, $currentVersion, $newVersion ) ); } $baseDir = OC_App::getInstallPath() . '/' . $appId; // Remove old app with the ID if existent OC_Helper::rmdirr($baseDir); // Move to app folder if(@mkdir($baseDir)) { $extractDir .= '/' . $folders[0]; OC_Helper::copyr($extractDir, $baseDir); } OC_Helper::copyr($extractDir, $baseDir); OC_Helper::rmdirr($extractDir); return; } else { throw new \Exception( sprintf( 'Could not extract app with ID %s to %s', $appId, $extractDir ) ); } } else { // Signature does not match throw new \Exception( sprintf( 'App with id %s has invalid signature', $appId ) ); } } } throw new \Exception( sprintf( 'Could not download app %s', $appId ) ); } /** * Check if an update for the app is available * * @param string $appId * @param AppFetcher $appFetcher * @return string|false false or the version number of the update */ public static function isUpdateAvailable($appId, AppFetcher $appFetcher) { static $isInstanceReadyForUpdates = null; if ($isInstanceReadyForUpdates === null) { $installPath = OC_App::getInstallPath(); if ($installPath === false || $installPath === null) { $isInstanceReadyForUpdates = false; } else { $isInstanceReadyForUpdates = true; } } if ($isInstanceReadyForUpdates === false) { return false; } $apps = $appFetcher->get(); foreach($apps as $app) { if($app['id'] === $appId) { $currentVersion = OC_App::getAppVersion($appId); $newestVersion = $app['releases'][0]['version']; if (version_compare($newestVersion, $currentVersion, '>')) { return $newestVersion; } else { return false; } } } return false; } /** * Check if app is already downloaded * @param string $name name of the application to remove * @return boolean * * The function will check if the app is already downloaded in the apps repository */ public function isDownloaded($name) { foreach(\OC::$APPSROOTS as $dir) { $dirToTest = $dir['path']; $dirToTest .= '/'; $dirToTest .= $name; $dirToTest .= '/'; if (is_dir($dirToTest)) { return true; } } return false; } /** * Removes an app * @param string $appId ID of the application to remove * @return boolean * * * This function works as follows * -# call uninstall repair steps * -# removing the files * * The function will not delete preferences, tables and the configuration, * this has to be done by the function oc_app_uninstall(). */ public function removeApp($appId) { if($this->isDownloaded( $appId )) { $appDir = OC_App::getInstallPath() . '/' . $appId; OC_Helper::rmdirr($appDir); return true; }else{ \OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR); return false; } } /** * Installs the app within the bundle and marks the bundle as installed * * @param Bundle $bundle * @throws \Exception If app could not get installed */ public function installAppBundle(Bundle $bundle) { $appIds = $bundle->getAppIdentifiers(); foreach($appIds as $appId) { if(!$this->isDownloaded($appId)) { $this->downloadApp($appId); } $this->installApp($appId); $app = new OC_App(); $app->enable($appId); } $bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true); $bundles[] = $bundle->getIdentifier(); $this->config->setAppValue('core', 'installed.bundles', json_encode($bundles)); } /** * Installs shipped apps * * This function installs all apps found in the 'apps' directory that should be enabled by default; * @param bool $softErrors When updating we ignore errors and simply log them, better to have a * working ownCloud at the end instead of an aborted update. * @return array Array of error messages (appid => Exception) */ public static function installShippedApps($softErrors = false) { $errors = []; foreach(\OC::$APPSROOTS as $app_dir) { if($dir = opendir( $app_dir['path'] )) { while( false !== ( $filename = readdir( $dir ))) { if( substr( $filename, 0, 1 ) != '.' and is_dir($app_dir['path']."/$filename") ) { if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) { if(!Installer::isInstalled($filename)) { $info=OC_App::getAppInfo($filename); $enabled = isset($info['default_enable']); if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps())) && \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') { if ($softErrors) { try { Installer::installShippedApp($filename); } catch (HintException $e) { if ($e->getPrevious() instanceof TableExistsException) { $errors[$filename] = $e; continue; } throw $e; } } else { Installer::installShippedApp($filename); } \OC::$server->getConfig()->setAppValue($filename, 'enabled', 'yes'); } } } } } closedir( $dir ); } } return $errors; } /** * install an app already placed in the app folder * @param string $app id of the app to install * @return integer */ public static function installShippedApp($app) { //install the database $appPath = OC_App::getAppPath($app); if(is_file("$appPath/appinfo/database.xml")) { try { OC_DB::createDbFromStructure("$appPath/appinfo/database.xml"); } catch (TableExistsException $e) { throw new HintException( 'Failed to enable app ' . $app, 'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer">support channels</a>.', 0, $e ); } } //run appinfo/install.php \OC_App::registerAutoloading($app, $appPath); self::includeAppScript("$appPath/appinfo/install.php"); $info = OC_App::getAppInfo($app); if (is_null($info)) { return false; } \OC_App::setupBackgroundJobs($info['background-jobs']); OC_App::executeRepairSteps($app, $info['repair-steps']['install']); $config = \OC::$server->getConfig(); $config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app)); if (array_key_exists('ocsid', $info)) { $config->setAppValue($app, 'ocsid', $info['ocsid']); } //set remote/public handlers foreach($info['remote'] as $name=>$path) { $config->setAppValue('core', 'remote_'.$name, $app.'/'.$path); } foreach($info['public'] as $name=>$path) { $config->setAppValue('core', 'public_'.$name, $app.'/'.$path); } OC_App::setAppTypes($info['id']); if(isset($info['settings']) && is_array($info['settings'])) { // requires that autoloading was registered for the app, // as happens before running the install.php some lines above \OC::$server->getSettingsManager()->setupSettings($info['settings']); } return $info['id']; } /** * check the code of an app with some static code checks * @param string $folder the folder of the app to check * @return boolean true for app is o.k. and false for app is not o.k. */ public static function checkCode($folder) { // is the code checker enabled? if(!\OC::$server->getConfig()->getSystemValue('appcodechecker', false)) { return true; } $codeChecker = new CodeChecker(new PrivateCheck(new EmptyCheck())); $errors = $codeChecker->analyseFolder(basename($folder), $folder); return empty($errors); } /** * @param string $script */ private static function includeAppScript($script) { if ( file_exists($script) ){ include $script; } } } private/PreviewNotAvailableException.php 0000604 00000001643 15247130451 0014513 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> * * @author Morris Jobke <hey@morrisjobke.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC; class PreviewNotAvailableException extends \Exception { } private/Preview/Provider.php 0000604 00000004212 15247130452 0012140 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; use OCP\Preview\IProvider; abstract class Provider implements IProvider { private $options; /** * Constructor * * @param array $options */ public function __construct(array $options = []) { $this->options = $options; } /** * @return string Regex with the mimetypes that are supported by this provider */ abstract public function getMimeType(); /** * Check if a preview can be generated for $path * * @param \OCP\Files\FileInfo $file * @return bool */ public function isAvailable(\OCP\Files\FileInfo $file) { return true; } /** * Generates thumbnail which fits in $maxX and $maxY and keeps the aspect ratio, for file at path $path * * @param string $path Path of file * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image * @param bool $scalingup Disable/Enable upscaling of previews * @param \OC\Files\View $fileview fileview object of user folder * @return bool|\OCP\IImage false if no preview was generated */ abstract public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview); } private/Preview/BMP.php 0000604 00000001575 15247130452 0010775 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Olivier Paroz <github@oparoz.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class BMP extends Image { /** * {@inheritDoc} */ public function getMimeType() { return '/image\/bmp/'; } } private/Preview/Font.php 0000604 00000001655 15247130452 0011264 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Olivier Paroz <github@oparoz.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; // .otf, .ttf and .pfb class Font extends Bitmap { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/(?:font-sfnt|x-font$)/'; } } private/Preview/MSOffice2003.php 0000604 00000001743 15247130452 0012314 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.docm, .dotm, .xls(m), .xlt(m), .xla(m), .ppt(m), .pot(m), .pps(m), .ppa(m) class MSOffice2003 extends Office { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/vnd.ms-.*/'; } } private/Preview/Generator.php 0000604 00000023222 15247130452 0012276 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Preview; use OCP\Files\File; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IConfig; use OCP\IImage; use OCP\IPreview; use OCP\Preview\IProvider; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; class Generator { /** @var IPreview */ private $previewManager; /** @var IConfig */ private $config; /** @var IAppData */ private $appData; /** @var GeneratorHelper */ private $helper; /** @var EventDispatcherInterface */ private $eventDispatcher; /** * @param IConfig $config * @param IPreview $previewManager * @param IAppData $appData * @param GeneratorHelper $helper * @param EventDispatcherInterface $eventDispatcher */ public function __construct( IConfig $config, IPreview $previewManager, IAppData $appData, GeneratorHelper $helper, EventDispatcherInterface $eventDispatcher ) { $this->config = $config; $this->previewManager = $previewManager; $this->appData = $appData; $this->helper = $helper; $this->eventDispatcher = $eventDispatcher; } /** * Returns a preview of a file * * The cache is searched first and if nothing usable was found then a preview is * generated by one of the providers * * @param File $file * @param int $width * @param int $height * @param bool $crop * @param string $mode * @param string $mimeType * @return ISimpleFile * @throws NotFoundException * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) */ public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) { $this->eventDispatcher->dispatch( IPreview::EVENT, new GenericEvent($file,[ 'width' => $width, 'height' => $height, 'crop' => $crop, 'mode' => $mode ]) ); if ($mimeType === null) { $mimeType = $file->getMimeType(); } if (!$this->previewManager->isMimeSupported($mimeType)) { throw new NotFoundException(); } $previewFolder = $this->getPreviewFolder($file); // Get the max preview and infer the max preview sizes from that $maxPreview = $this->getMaxPreview($previewFolder, $file, $mimeType); list($maxWidth, $maxHeight) = $this->getPreviewSize($maxPreview); // Calculate the preview size list($width, $height) = $this->calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight); // No need to generate a preview that is just the max preview if ($width === $maxWidth && $height === $maxHeight) { return $maxPreview; } // Try to get a cached preview. Else generate (and store) one try { $file = $this->getCachedPreview($previewFolder, $width, $height, $crop); } catch (NotFoundException $e) { $file = $this->generatePreview($previewFolder, $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight); } return $file; } /** * @param ISimpleFolder $previewFolder * @param File $file * @param string $mimeType * @return ISimpleFile * @throws NotFoundException */ private function getMaxPreview(ISimpleFolder $previewFolder, File $file, $mimeType) { $nodes = $previewFolder->getDirectoryListing(); foreach ($nodes as $node) { if (strpos($node->getName(), 'max')) { return $node; } } $previewProviders = $this->previewManager->getProviders(); foreach ($previewProviders as $supportedMimeType => $providers) { if (!preg_match($supportedMimeType, $mimeType)) { continue; } foreach ($providers as $provider) { $provider = $this->helper->getProvider($provider); if (!($provider instanceof IProvider)) { continue; } $maxWidth = (int)$this->config->getSystemValue('preview_max_x', 2048); $maxHeight = (int)$this->config->getSystemValue('preview_max_y', 2048); $preview = $this->helper->getThumbnail($provider, $file, $maxWidth, $maxHeight); if (!($preview instanceof IImage)) { continue; } $path = (string)$preview->width() . '-' . (string)$preview->height() . '-max.png'; try { $file = $previewFolder->newFile($path); $file->putContent($preview->data()); } catch (NotPermittedException $e) { throw new NotFoundException(); } return $file; } } throw new NotFoundException(); } /** * @param ISimpleFile $file * @return int[] */ private function getPreviewSize(ISimpleFile $file) { $size = explode('-', $file->getName()); return [(int)$size[0], (int)$size[1]]; } /** * @param int $width * @param int $height * @param bool $crop * @return string */ private function generatePath($width, $height, $crop) { $path = (string)$width . '-' . (string)$height; if ($crop) { $path .= '-crop'; } $path .= '.png'; return $path; } /** * @param int $width * @param int $height * @param bool $crop * @param string $mode * @param int $maxWidth * @param int $maxHeight * @return int[] */ private function calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight) { /* * If we are not cropping we have to make sure the requested image * respects the aspect ratio of the original. */ if (!$crop) { $ratio = $maxHeight / $maxWidth; if ($width === -1) { $width = $height / $ratio; } if ($height === -1) { $height = $width * $ratio; } $ratioH = $height / $maxHeight; $ratioW = $width / $maxWidth; /* * Fill means that the $height and $width are the max * Cover means min. */ if ($mode === IPreview::MODE_FILL) { if ($ratioH > $ratioW) { $height = $width * $ratio; } else { $width = $height / $ratio; } } else if ($mode === IPreview::MODE_COVER) { if ($ratioH > $ratioW) { $width = $height / $ratio; } else { $height = $width * $ratio; } } } if ($height !== $maxHeight && $width !== $maxWidth) { /* * Scale to the nearest power of two */ $pow2height = 2 ** ceil(log($height) / log(2)); $pow2width = 2 ** ceil(log($width) / log(2)); $ratioH = $height / $pow2height; $ratioW = $width / $pow2width; if ($ratioH < $ratioW) { $width = $pow2width; $height /= $ratioW; } else { $height = $pow2height; $width /= $ratioH; } } /* * Make sure the requested height and width fall within the max * of the preview. */ if ($height > $maxHeight) { $ratio = $height / $maxHeight; $height = $maxHeight; $width /= $ratio; } if ($width > $maxWidth) { $ratio = $width / $maxWidth; $width = $maxWidth; $height /= $ratio; } return [(int)round($width), (int)round($height)]; } /** * @param ISimpleFolder $previewFolder * @param ISimpleFile $maxPreview * @param int $width * @param int $height * @param bool $crop * @param int $maxWidth * @param int $maxHeight * @return ISimpleFile * @throws NotFoundException * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) */ private function generatePreview(ISimpleFolder $previewFolder, ISimpleFile $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight) { $preview = $this->helper->getImage($maxPreview); if (!$preview->valid()) { throw new \InvalidArgumentException('Failed to generate preview, failed to load image'); } if ($crop) { if ($height !== $preview->height() && $width !== $preview->width()) { //Resize $widthR = $preview->width() / $width; $heightR = $preview->height() / $height; if ($widthR > $heightR) { $scaleH = $height; $scaleW = $maxWidth / $heightR; } else { $scaleH = $maxHeight / $widthR; $scaleW = $width; } $preview->preciseResize(round($scaleW), round($scaleH)); } $cropX = floor(abs($width - $preview->width()) * 0.5); $cropY = 0; $preview->crop($cropX, $cropY, $width, $height); } else { $preview->resize(max($width, $height)); } $path = $this->generatePath($width, $height, $crop); try { $file = $previewFolder->newFile($path); $file->putContent($preview->data()); } catch (NotPermittedException $e) { throw new NotFoundException(); } return $file; } /** * @param ISimpleFolder $previewFolder * @param int $width * @param int $height * @param bool $crop * @return ISimpleFile * * @throws NotFoundException */ private function getCachedPreview(ISimpleFolder $previewFolder, $width, $height, $crop) { $path = $this->generatePath($width, $height, $crop); return $previewFolder->getFile($path); } /** * Get the specific preview folder for this file * * @param File $file * @return ISimpleFolder */ private function getPreviewFolder(File $file) { try { $folder = $this->appData->getFolder($file->getId()); } catch (NotFoundException $e) { $folder = $this->appData->newFolder($file->getId()); } return $folder; } } private/Preview/JPEG.php 0000604 00000001577 15247130452 0011106 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Olivier Paroz <github@oparoz.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class JPEG extends Image { /** * {@inheritDoc} */ public function getMimeType() { return '/image\/jpeg/'; } } private/Preview/MP3.php 0000604 00000003442 15247130452 0010751 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * @author Hendrik Leppelsack <hendrik@leppelsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Olivier Paroz <github@oparoz.com> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; use ID3Parser\ID3Parser; class MP3 extends Provider { /** * {@inheritDoc} */ public function getMimeType() { return '/audio\/mpeg/'; } /** * {@inheritDoc} */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { $getID3 = new ID3Parser(); $tmpPath = $fileview->toTmpFile($path); $tags = $getID3->analyze($tmpPath); unlink($tmpPath); $picture = isset($tags['id3v2']['APIC'][0]['data']) ? $tags['id3v2']['APIC'][0]['data'] : null; if(is_null($picture) && isset($tags['id3v2']['PIC'][0]['data'])) { $picture = $tags['id3v2']['PIC'][0]['data']; } if(!is_null($picture)) { $image = new \OC_Image(); $image->loadFromData($picture); if ($image->valid()) { $image->scaleDownToFit($maxX, $maxY); return $image; } } return false; } } private/Preview/Photoshop.php 0000604 00000001711 15247130452 0012332 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.psd class Photoshop extends Bitmap { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/x-photoshop/'; } } private/Preview/StarOffice.php 0000604 00000001724 15247130452 0012400 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.sxw, .stw, .sxc, .stc, .sxd, .std, .sxi, .sti, .sxg, .sxm class StarOffice extends Office { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/vnd.sun.xml.*/'; } } private/Preview/GIF.php 0000604 00000001575 15247130452 0010764 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Olivier Paroz <github@oparoz.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class GIF extends Image { /** * {@inheritDoc} */ public function getMimeType() { return '/image\/gif/'; } } private/Preview/Movie.php 0000604 00000006175 15247130452 0011437 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class Movie extends Provider { public static $avconvBinary; public static $ffmpegBinary; /** * {@inheritDoc} */ public function getMimeType() { return '/video\/.*/'; } /** * {@inheritDoc} */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { // TODO: use proc_open() and stream the source file ? $fileInfo = $fileview->getFileInfo($path); $useFileDirectly = (!$fileInfo->isEncrypted() && !$fileInfo->isMounted()); if ($useFileDirectly) { $absPath = $fileview->getLocalFile($path); } else { $absPath = \OC::$server->getTempManager()->getTemporaryFile(); $handle = $fileview->fopen($path, 'rb'); // we better use 5MB (1024 * 1024 * 5 = 5242880) instead of 1MB. // in some cases 1MB was no enough to generate thumbnail $firstmb = stream_get_contents($handle, 5242880); file_put_contents($absPath, $firstmb); } $result = $this->generateThumbNail($maxX, $maxY, $absPath, 5); if ($result === false) { $result = $this->generateThumbNail($maxX, $maxY, $absPath, 1); if ($result === false) { $result = $this->generateThumbNail($maxX, $maxY, $absPath, 0); } } if (!$useFileDirectly) { unlink($absPath); } return $result; } /** * @param int $maxX * @param int $maxY * @param string $absPath * @param int $second * @return bool|\OCP\IImage */ private function generateThumbNail($maxX, $maxY, $absPath, $second) { $tmpPath = \OC::$server->getTempManager()->getTemporaryFile(); if (self::$avconvBinary) { $cmd = self::$avconvBinary . ' -y -ss ' . escapeshellarg($second) . ' -i ' . escapeshellarg($absPath) . ' -an -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) . ' > /dev/null 2>&1'; } else { $cmd = self::$ffmpegBinary . ' -y -ss ' . escapeshellarg($second) . ' -i ' . escapeshellarg($absPath) . ' -f mjpeg -vframes 1' . ' ' . escapeshellarg($tmpPath) . ' > /dev/null 2>&1'; } exec($cmd, $output, $returnCode); if ($returnCode === 0) { $image = new \OC_Image(); $image->loadFromFile($tmpPath); unlink($tmpPath); if ($image->valid()) { $image->scaleDownToFit($maxX, $maxY); return $image; } } unlink($tmpPath); return false; } } private/Preview/PDF.php 0000604 00000001673 15247130452 0010767 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.pdf class PDF extends Bitmap { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/pdf/'; } } private/Preview/Office.php 0000604 00000005613 15247130452 0011547 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; abstract class Office extends Provider { private $cmd; /** * {@inheritDoc} */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { $this->initCmd(); if (is_null($this->cmd)) { return false; } $absPath = $fileview->toTmpFile($path); $tmpDir = \OC::$server->getTempManager()->getTempBaseDir(); $defaultParameters = ' -env:UserInstallation=file://' . escapeshellarg($tmpDir . '/owncloud-' . \OC_Util::getInstanceId() . '/') . ' --headless --nologo --nofirststartwizard --invisible --norestore --convert-to pdf --outdir '; $clParameters = \OCP\Config::getSystemValue('preview_office_cl_parameters', $defaultParameters); $exec = $this->cmd . $clParameters . escapeshellarg($tmpDir) . ' ' . escapeshellarg($absPath); shell_exec($exec); //create imagick object from pdf $pdfPreview = null; try { list($dirname, , , $filename) = array_values(pathinfo($absPath)); $pdfPreview = $dirname . '/' . $filename . '.pdf'; $pdf = new \imagick($pdfPreview . '[0]'); $pdf->setImageFormat('jpg'); } catch (\Exception $e) { unlink($absPath); unlink($pdfPreview); \OCP\Util::writeLog('core', $e->getmessage(), \OCP\Util::ERROR); return false; } $image = new \OC_Image(); $image->loadFromData($pdf); unlink($absPath); unlink($pdfPreview); if ($image->valid()) { $image->scaleDownToFit($maxX, $maxY); return $image; } return false; } private function initCmd() { $cmd = ''; $libreOfficePath = \OC::$server->getConfig()->getSystemValue('preview_libreoffice_path', null); if (is_string($libreOfficePath)) { $cmd = $libreOfficePath; } $whichLibreOffice = shell_exec('command -v libreoffice'); if ($cmd === '' && !empty($whichLibreOffice)) { $cmd = 'libreoffice'; } $whichOpenOffice = shell_exec('command -v openoffice'); if ($cmd === '' && !empty($whichOpenOffice)) { $cmd = 'openoffice'; } if ($cmd === '') { $cmd = null; } $this->cmd = $cmd; } } private/Preview/MSOfficeDoc.php 0000604 00000001636 15247130452 0012436 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.doc, .dot class MSOfficeDoc extends Office { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/msword/'; } } private/Preview/MarkDown.php 0000604 00000001614 15247130452 0012073 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class MarkDown extends TXT { /** * {@inheritDoc} */ public function getMimeType() { return '/text\/(x-)?markdown/'; } } private/Preview/WatcherConnector.php 0000604 00000003646 15247130452 0013630 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Preview; use OC\SystemConfig; use OCP\Files\Node; use OCP\Files\IRootFolder; class WatcherConnector { /** @var IRootFolder */ private $root; /** @var SystemConfig */ private $config; /** * WatcherConnector constructor. * * @param IRootFolder $root * @param SystemConfig $config */ public function __construct(IRootFolder $root, SystemConfig $config) { $this->root = $root; $this->config = $config; } /** * @return Watcher */ private function getWatcher() { return \OC::$server->query(Watcher::class); } public function connectWatcher() { // Do not connect if we are not setup yet! if ($this->config->getValue('instanceid', null) !== null) { $this->root->listen('\OC\Files', 'postWrite', function (Node $node) { $this->getWatcher()->postWrite($node); }); $this->root->listen('\OC\Files', 'preDelete', function (Node $node) { $this->getWatcher()->preDelete($node); }); $this->root->listen('\OC\Files', 'postDelete', function (Node $node) { $this->getWatcher()->postDelete($node); }); } } } private/Preview/TXT.php 0000604 00000004600 15247130452 0011026 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Nmz <nemesiz@nmz.lt> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class TXT extends Provider { /** * {@inheritDoc} */ public function getMimeType() { return '/text\/plain/'; } /** * {@inheritDoc} */ public function isAvailable(\OCP\Files\FileInfo $file) { return $file->getSize() > 0; } /** * {@inheritDoc} */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { $content = $fileview->fopen($path, 'r'); $content = stream_get_contents($content,3000); //don't create previews of empty text files if(trim($content) === '') { return false; } $lines = preg_split("/\r\n|\n|\r/", $content); $fontSize = ($maxX) ? (int) ((5 / 32) * $maxX) : 5; //5px $lineSize = ceil($fontSize * 1.25); $image = imagecreate($maxX, $maxY); imagecolorallocate($image, 255, 255, 255); $textColor = imagecolorallocate($image, 0, 0, 0); $fontFile = __DIR__; $fontFile .= '/../../../core'; $fontFile .= '/fonts/OpenSans-Regular.ttf'; $canUseTTF = function_exists('imagettftext'); foreach($lines as $index => $line) { $index = $index + 1; $x = (int) 1; $y = (int) ($index * $lineSize); if ($canUseTTF === true) { imagettftext($image, $fontSize, 0, $x, $y, $textColor, $fontFile, $line); } else { $y -= $fontSize; imagestring($image, 1, $x, $y, $line, $textColor); } if(($index * $lineSize) >= $maxY) { break; } } $image = new \OC_Image($image); return $image->valid() ? $image : false; } } private/Preview/TIFF.php 0000604 00000001670 15247130452 0011103 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.tiff class TIFF extends Bitmap { /** * {@inheritDoc} */ public function getMimeType() { return '/image\/tiff/'; } } private/Preview/XBitmap.php 0000604 00000001607 15247130452 0011717 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Olivier Paroz <github@oparoz.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class XBitmap extends Image { /** * {@inheritDoc} */ public function getMimeType() { return '/image\/x-xbitmap/'; } } private/Preview/Bitmap.php 0000604 00000006447 15247130452 0011576 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; use Imagick; /** * Creates a PNG preview using ImageMagick via the PECL extension * * @package OC\Preview */ abstract class Bitmap extends Provider { /** * {@inheritDoc} */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { $tmpPath = $fileview->toTmpFile($path); if (!$tmpPath) { return false; } // Creates \Imagick object from bitmap or vector file try { $bp = $this->getResizedPreview($tmpPath, $maxX, $maxY); } catch (\Exception $e) { \OCP\Util::writeLog('core', 'ImageMagick says: ' . $e->getmessage(), \OCP\Util::ERROR); return false; } unlink($tmpPath); //new bitmap image object $image = new \OC_Image(); $image->loadFromData($bp); //check if image object is valid return $image->valid() ? $image : false; } /** * Returns a preview of maxX times maxY dimensions in PNG format * * * The default resolution is already 72dpi, no need to change it for a bitmap output * * It's possible to have proper colour conversion using profileimage(). * ICC profiles are here: http://www.color.org/srgbprofiles.xalter * * It's possible to Gamma-correct an image via gammaImage() * * @param string $tmpPath the location of the file to convert * @param int $maxX * @param int $maxY * * @return \Imagick */ private function getResizedPreview($tmpPath, $maxX, $maxY) { $bp = new Imagick(); // Layer 0 contains either the bitmap or a flat representation of all vector layers $bp->readImage($tmpPath . '[0]'); $bp = $this->resize($bp, $maxX, $maxY); $bp->setImageFormat('png'); return $bp; } /** * Returns a resized \Imagick object * * If you want to know more on the various methods available to resize an * image, check out this link : @link https://stackoverflow.com/questions/8517304/what-the-difference-of-sample-resample-scale-resize-adaptive-resize-thumbnail-im * * @param \Imagick $bp * @param int $maxX * @param int $maxY * * @return \Imagick */ private function resize($bp, $maxX, $maxY) { list($previewWidth, $previewHeight) = array_values($bp->getImageGeometry()); // We only need to resize a preview which doesn't fit in the maximum dimensions if ($previewWidth > $maxX || $previewHeight > $maxY) { // TODO: LANCZOS is the default filter, CATROM could bring similar results faster $bp->resizeImage($maxX, $maxY, imagick::FILTER_LANCZOS, 1, true); } return $bp; } } private/Preview/SVG.php 0000604 00000003667 15247130452 0011022 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Olivier Paroz <github@oparoz.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class SVG extends Provider { /** * {@inheritDoc} */ public function getMimeType() { return '/image\/svg\+xml/'; } /** * {@inheritDoc} */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { try { $svg = new \Imagick(); $svg->setBackgroundColor(new \ImagickPixel('transparent')); $content = stream_get_contents($fileview->fopen($path, 'r')); if (substr($content, 0, 5) !== '<?xml') { $content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content; } // Do not parse SVG files with references if (stripos($content, 'xlink:href') !== false) { return false; } $svg->readImageBlob($content); $svg->setImageFormat('png32'); } catch (\Exception $e) { \OCP\Util::writeLog('core', $e->getmessage(), \OCP\Util::ERROR); return false; } //new image object $image = new \OC_Image(); $image->loadFromData($svg); //check if image object is valid if ($image->valid()) { $image->scaleDownToFit($maxX, $maxY); return $image; } return false; } } private/Preview/Postscript.php 0000604 00000001711 15247130452 0012521 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.eps class Postscript extends Bitmap { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/postscript/'; } } private/Preview/MSOffice2007.php 0000604 00000001741 15247130452 0012316 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.docx, .dotx, .xlsx, .xltx, .pptx, .potx, .ppsx class MSOffice2007 extends Office { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/vnd.openxmlformats-officedocument.*/'; } } private/Preview/PNG.php 0000604 00000001575 15247130452 0011003 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Olivier Paroz <github@oparoz.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; class PNG extends Image { /** * {@inheritDoc} */ public function getMimeType() { return '/image\/png/'; } } private/Preview/OpenDocument.php 0000604 00000001777 15247130452 0012763 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.odt, .ott, .oth, .odm, .odg, .otg, .odp, .otp, .ods, .ots, .odc, .odf, .odb, .odi, .oxt class OpenDocument extends Office { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/vnd.oasis.opendocument.*/'; } } private/Preview/Illustrator.php 0000604 00000001712 15247130452 0012674 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; //.ai class Illustrator extends Bitmap { /** * {@inheritDoc} */ public function getMimeType() { return '/application\/illustrator/'; } } private/Preview/Watcher.php 0000604 00000004571 15247130452 0011753 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Preview; use OCP\Files\File; use OCP\Files\Node; use OCP\Files\Folder; use OCP\Files\IAppData; use OCP\Files\NotFoundException; /** * Class Watcher * * @package OC\Preview * * Class that will watch filesystem activity and remove previews as needed. */ class Watcher { /** @var IAppData */ private $appData; /** @var int[] */ private $toDelete = []; /** * Watcher constructor. * * @param IAppData $appData */ public function __construct(IAppData $appData) { $this->appData = $appData; } public function postWrite(Node $node) { // We only handle files if ($node instanceof Folder) { return; } try { $folder = $this->appData->getFolder($node->getId()); $folder->delete(); } catch (NotFoundException $e) { //Nothing to do } } public function preDelete(Node $node) { // To avoid cycles if ($this->toDelete !== []) { return; } if ($node instanceof File) { $this->toDelete[] = $node->getId(); return; } /** @var Folder $node */ $this->deleteFolder($node); } private function deleteFolder(Folder $folder) { $nodes = $folder->getDirectoryListing(); foreach ($nodes as $node) { if ($node instanceof File) { $this->toDelete[] = $node->getId(); } else if ($node instanceof Folder) { $this->deleteFolder($node); } } } public function postDelete(Node $node) { foreach ($this->toDelete as $fid) { try { $folder = $this->appData->getFolder($fid); $folder->delete(); } catch (NotFoundException $e) { // continue } } } } private/Preview/Image.php 0000604 00000003720 15247130452 0011373 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author josh4trunks <joshruehlig@gmail.com> * @author Olivier Paroz <github@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Preview; abstract class Image extends Provider { /** * {@inheritDoc} */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { //get fileinfo $fileInfo = $fileview->getFileInfo($path); if (!$fileInfo) { return false; } $maxSizeForImages = \OC::$server->getConfig()->getSystemValue('preview_max_filesize_image', 50); $size = $fileInfo->getSize(); if ($maxSizeForImages !== -1 && $size > ($maxSizeForImages * 1024 * 1024)) { return false; } $image = new \OC_Image(); $useTempFile = $fileInfo->isEncrypted() || !$fileInfo->getStorage()->isLocal(); if ($useTempFile) { $fileName = $fileview->toTmpFile($path); } else { $fileName = $fileview->getLocalFile($path); } $image->loadFromFile($fileName); $image->fixOrientation(); if ($useTempFile) { unlink($fileName); } if ($image->valid()) { $image->scaleDownToFit($maxX, $maxY); return $image; } return false; } } private/Preview/GeneratorHelper.php 0000604 00000004641 15247130452 0013442 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Preview; use OC\Files\View; use OCP\Files\File; use OCP\Files\IRootFolder; use OCP\Files\SimpleFS\ISimpleFile; use OCP\IImage; use OCP\Image as img; use OCP\Preview\IProvider; /** * Very small wrapper class to make the generator fully unit testable */ class GeneratorHelper { /** @var IRootFolder */ private $rootFolder; public function __construct(IRootFolder $rootFolder) { $this->rootFolder = $rootFolder; } /** * @param IProvider $provider * @param File $file * @param int $maxWidth * @param int $maxHeight * @return bool|IImage */ public function getThumbnail(IProvider $provider, File $file, $maxWidth, $maxHeight) { list($view, $path) = $this->getViewAndPath($file); return $provider->getThumbnail($path, $maxWidth, $maxHeight, false, $view); } /** * @param File $file * @return array * This is required to create the old view and path */ private function getViewAndPath(File $file) { $absPath = ltrim($file->getPath(), '/'); $owner = explode('/', $absPath)[0]; $userFolder = $this->rootFolder->getUserFolder($owner)->getParent(); $nodes = $userFolder->getById($file->getId()); $file = $nodes[0]; $view = new View($userFolder->getPath()); $path = $userFolder->getRelativePath($file->getPath()); return [$view, $path]; } /** * @param ISimpleFile $maxPreview * @return IImage */ public function getImage(ISimpleFile $maxPreview) { return new img($maxPreview->getContent()); } /** * @param $provider * @return IProvider */ public function getProvider($provider) { return $provider(); } } private/Tagging/TagMapper.php 0000604 00000004477 15247130452 0012202 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Bernhard Reiter <ockham@raz.or.at> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Tagging; use \OCP\AppFramework\Db\Mapper, \OCP\AppFramework\Db\DoesNotExistException, \OCP\IDBConnection; /** * Mapper for Tag entity */ class TagMapper extends Mapper { /** * Constructor. * * @param IDBConnection $db Instance of the Db abstraction layer. */ public function __construct(IDBConnection $db) { parent::__construct($db, 'vcategory', 'OC\Tagging\Tag'); } /** * Load tags from the database. * * @param array|string $owners The user(s) whose tags we are going to load. * @param string $type The type of item for which we are loading tags. * @return array An array of Tag objects. */ public function loadTags($owners, $type) { if(!is_array($owners)) { $owners = array($owners); } $sql = 'SELECT `id`, `uid`, `type`, `category` FROM `' . $this->getTableName() . '` ' . 'WHERE `uid` IN (' . str_repeat('?,', count($owners)-1) . '?) AND `type` = ? ORDER BY `category`'; return $this->findEntities($sql, array_merge($owners, array($type))); } /** * Check if a given Tag object already exists in the database. * * @param Tag $tag The tag to look for in the database. * @return bool */ public function tagExists($tag) { $sql = 'SELECT `id`, `uid`, `type`, `category` FROM `' . $this->getTableName() . '` ' . 'WHERE `uid` = ? AND `type` = ? AND `category` = ?'; try { $this->findEntity($sql, array($tag->getOwner(), $tag->getType(), $tag->getName())); } catch (DoesNotExistException $e) { return false; } return true; } } private/Tagging/Tag.php 0000604 00000004515 15247130452 0011026 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Reiter <ockham@raz.or.at> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Tagging; use \OCP\AppFramework\Db\Entity; /** * Class to represent a tag. * * @method string getOwner() * @method void setOwner(string $owner) * @method string getType() * @method void setType(string $type) * @method string getName() * @method void setName(string $name) */ class Tag extends Entity { protected $owner; protected $type; protected $name; /** * Constructor. * * @param string $owner The tag's owner * @param string $type The type of item this tag is used for * @param string $name The tag's name */ public function __construct($owner = null, $type = null, $name = null) { $this->setOwner($owner); $this->setType($type); $this->setName($name); } /** * Transform a database columnname to a property * * @param string $columnName the name of the column * @return string the property name * @todo migrate existing database columns to the correct names * to be able to drop this direct mapping */ public function columnToProperty($columnName){ if ($columnName === 'category') { return 'name'; } elseif ($columnName === 'uid') { return 'owner'; } else { return parent::columnToProperty($columnName); } } /** * Transform a property to a database column name * * @param string $property the name of the property * @return string the column name */ public function propertyToColumn($property){ if ($property === 'name') { return 'category'; } elseif ($property === 'owner') { return 'uid'; } else { return parent::propertyToColumn($property); } } } private/LargeFileHelper.php 0000604 00000013755 15247130452 0011733 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> * * @author Andreas Fischer <bantu@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Roitzsch <reactorcontrol@icloud.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; /** * Helper class for large files on 32-bit platforms. */ class LargeFileHelper { /** * pow(2, 53) as a base-10 string. * @var string */ const POW_2_53 = '9007199254740992'; /** * pow(2, 53) - 1 as a base-10 string. * @var string */ const POW_2_53_MINUS_1 = '9007199254740991'; /** * @brief Checks whether our assumptions hold on the PHP platform we are on. * * @throws \RunTimeException if our assumptions do not hold on the current * PHP platform. */ public function __construct() { $pow_2_53 = floatval(self::POW_2_53_MINUS_1) + 1.0; if ($this->formatUnsignedInteger($pow_2_53) !== self::POW_2_53) { throw new \RuntimeException( 'This class assumes floats to be double precision or "better".' ); } } /** * @brief Formats a signed integer or float as an unsigned integer base-10 * string. Passed strings will be checked for being base-10. * * @param int|float|string $number Number containing unsigned integer data * * @throws \UnexpectedValueException if $number is not a float, not an int * and not a base-10 string. * * @return string Unsigned integer base-10 string */ public function formatUnsignedInteger($number) { if (is_float($number)) { // Undo the effect of the php.ini setting 'precision'. return number_format($number, 0, '', ''); } else if (is_string($number) && ctype_digit($number)) { return $number; } else if (is_int($number)) { // Interpret signed integer as unsigned integer. return sprintf('%u', $number); } else { throw new \UnexpectedValueException( 'Expected int, float or base-10 string' ); } } /** * @brief Tries to get the size of a file via various workarounds that * even work for large files on 32-bit platforms. * * @param string $filename Path to the file. * * @return null|int|float Number of bytes as number (float or int) or * null on failure. */ public function getFileSize($filename) { $fileSize = $this->getFileSizeViaCurl($filename); if (!is_null($fileSize)) { return $fileSize; } $fileSize = $this->getFileSizeViaExec($filename); if (!is_null($fileSize)) { return $fileSize; } return $this->getFileSizeNative($filename); } /** * @brief Tries to get the size of a file via a CURL HEAD request. * * @param string $fileName Path to the file. * * @return null|int|float Number of bytes as number (float or int) or * null on failure. */ public function getFileSizeViaCurl($fileName) { if (\OC::$server->getIniWrapper()->getString('open_basedir') === '') { $encodedFileName = rawurlencode($fileName); $ch = curl_init("file://$encodedFileName"); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, true); $data = curl_exec($ch); curl_close($ch); if ($data !== false) { $matches = array(); preg_match('/Content-Length: (\d+)/', $data, $matches); if (isset($matches[1])) { return 0 + $matches[1]; } } } return null; } /** * @brief Tries to get the size of a file via an exec() call. * * @param string $filename Path to the file. * * @return null|int|float Number of bytes as number (float or int) or * null on failure. */ public function getFileSizeViaExec($filename) { if (\OC_Helper::is_function_enabled('exec')) { $os = strtolower(php_uname('s')); $arg = escapeshellarg($filename); $result = null; if (strpos($os, 'linux') !== false) { $result = $this->exec("stat -c %s $arg"); } else if (strpos($os, 'bsd') !== false || strpos($os, 'darwin') !== false) { $result = $this->exec("stat -f %z $arg"); } return $result; } return null; } /** * @brief Gets the size of a file via a filesize() call and converts * negative signed int to positive float. As the result of filesize() * will wrap around after a file size of 2^32 bytes = 4 GiB, this * should only be used as a last resort. * * @param string $filename Path to the file. * * @return int|float Number of bytes as number (float or int). */ public function getFileSizeNative($filename) { $result = filesize($filename); if ($result < 0) { // For file sizes between 2 GiB and 4 GiB, filesize() will return a // negative int, as the PHP data type int is signed. Interpret the // returned int as an unsigned integer and put it into a float. return (float) sprintf('%u', $result); } return $result; } /** * Returns the current mtime for $fullPath * * @param string $fullPath * @return int */ public function getFileMtime($fullPath) { if (\OC_Helper::is_function_enabled('exec')) { $os = strtolower(php_uname('s')); if (strpos($os, 'linux') !== false) { return $this->exec('stat -c %Y ' . escapeshellarg($fullPath)); } } return filemtime($fullPath); } protected function exec($cmd) { $result = trim(exec($cmd)); return ctype_digit($result) ? 0 + $result : null; } } private/Authentication/TwoFactorAuth/Manager.php 0000604 00000022633 15247130452 0016017 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\TwoFactorAuth; use BadMethodCallException; use Exception; use OC; use OC\App\AppManager; use OC_App; use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Token\IProvider as TokenProvider; use OCP\Activity\IManager; use OCP\AppFramework\QueryException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\IConfig; use OCP\ILogger; use OCP\ISession; use OCP\IUser; class Manager { const SESSION_UID_KEY = 'two_factor_auth_uid'; const SESSION_UID_DONE = 'two_factor_auth_passed'; const BACKUP_CODES_APP_ID = 'twofactor_backupcodes'; const BACKUP_CODES_PROVIDER_ID = 'backup_codes'; const REMEMBER_LOGIN = 'two_factor_remember_login'; /** @var AppManager */ private $appManager; /** @var ISession */ private $session; /** @var IConfig */ private $config; /** @var IManager */ private $activityManager; /** @var ILogger */ private $logger; /** @var TokenProvider */ private $tokenProvider; /** @var ITimeFactory */ private $timeFactory; /** * @param AppManager $appManager * @param ISession $session * @param IConfig $config * @param IManager $activityManager * @param ILogger $logger * @param TokenProvider $tokenProvider * @param ITimeFactory $timeFactory */ public function __construct(AppManager $appManager, ISession $session, IConfig $config, IManager $activityManager, ILogger $logger, TokenProvider $tokenProvider, ITimeFactory $timeFactory) { $this->appManager = $appManager; $this->session = $session; $this->config = $config; $this->activityManager = $activityManager; $this->logger = $logger; $this->tokenProvider = $tokenProvider; $this->timeFactory = $timeFactory; } /** * Determine whether the user must provide a second factor challenge * * @param IUser $user * @return boolean */ public function isTwoFactorAuthenticated(IUser $user) { $twoFactorEnabled = ((int) $this->config->getUserValue($user->getUID(), 'core', 'two_factor_auth_disabled', 0)) === 0; return $twoFactorEnabled && count($this->getProviders($user)) > 0; } /** * Disable 2FA checks for the given user * * @param IUser $user */ public function disableTwoFactorAuthentication(IUser $user) { $this->config->setUserValue($user->getUID(), 'core', 'two_factor_auth_disabled', 1); } /** * Enable all 2FA checks for the given user * * @param IUser $user */ public function enableTwoFactorAuthentication(IUser $user) { $this->config->deleteUserValue($user->getUID(), 'core', 'two_factor_auth_disabled'); } /** * Get a 2FA provider by its ID * * @param IUser $user * @param string $challengeProviderId * @return IProvider|null */ public function getProvider(IUser $user, $challengeProviderId) { $providers = $this->getProviders($user, true); return isset($providers[$challengeProviderId]) ? $providers[$challengeProviderId] : null; } /** * @param IUser $user * @return IProvider|null the backup provider, if enabled for the given user */ public function getBackupProvider(IUser $user) { $providers = $this->getProviders($user, true); if (!isset($providers[self::BACKUP_CODES_PROVIDER_ID])) { return null; } return $providers[self::BACKUP_CODES_PROVIDER_ID]; } /** * Get the list of 2FA providers for the given user * * @param IUser $user * @param bool $includeBackupApp * @return IProvider[] * @throws Exception */ public function getProviders(IUser $user, $includeBackupApp = false) { $allApps = $this->appManager->getEnabledAppsForUser($user); $providers = []; foreach ($allApps as $appId) { if (!$includeBackupApp && $appId === self::BACKUP_CODES_APP_ID) { continue; } $info = $this->appManager->getAppInfo($appId); if (isset($info['two-factor-providers'])) { $providerClasses = $info['two-factor-providers']; foreach ($providerClasses as $class) { try { $this->loadTwoFactorApp($appId); $provider = OC::$server->query($class); $providers[$provider->getId()] = $provider; } catch (QueryException $exc) { // Provider class can not be resolved throw new Exception("Could not load two-factor auth provider $class"); } } } } return array_filter($providers, function ($provider) use ($user) { /* @var $provider IProvider */ return $provider->isTwoFactorAuthEnabledForUser($user); }); } /** * Load an app by ID if it has not been loaded yet * * @param string $appId */ protected function loadTwoFactorApp($appId) { if (!OC_App::isAppLoaded($appId)) { OC_App::loadApp($appId); } } /** * Verify the given challenge * * @param string $providerId * @param IUser $user * @param string $challenge * @return boolean */ public function verifyChallenge($providerId, IUser $user, $challenge) { $provider = $this->getProvider($user, $providerId); if (is_null($provider)) { return false; } $passed = $provider->verifyChallenge($user, $challenge); if ($passed) { if ($this->session->get(self::REMEMBER_LOGIN) === true) { // TODO: resolve cyclic dependency and use DI \OC::$server->getUserSession()->createRememberMeToken($user); } $this->session->remove(self::SESSION_UID_KEY); $this->session->remove(self::REMEMBER_LOGIN); $this->session->set(self::SESSION_UID_DONE, $user->getUID()); // Clear token from db $sessionId = $this->session->getId(); $token = $this->tokenProvider->getToken($sessionId); $tokenId = $token->getId(); $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $tokenId); $this->publishEvent($user, 'twofactor_success', [ 'provider' => $provider->getDisplayName(), ]); } else { $this->publishEvent($user, 'twofactor_failed', [ 'provider' => $provider->getDisplayName(), ]); } return $passed; } /** * Push a 2fa event the user's activity stream * * @param IUser $user * @param string $event */ private function publishEvent(IUser $user, $event, array $params) { $activity = $this->activityManager->generateEvent(); $activity->setApp('core') ->setType('security') ->setAuthor($user->getUID()) ->setAffectedUser($user->getUID()) ->setSubject($event, $params); try { $this->activityManager->publish($activity); } catch (BadMethodCallException $e) { $this->logger->warning('could not publish backup code creation activity', ['app' => 'core']); $this->logger->logException($e, ['app' => 'core']); } } /** * Check if the currently logged in user needs to pass 2FA * * @param IUser $user the currently logged in user * @return boolean */ public function needsSecondFactor(IUser $user = null) { if ($user === null) { return false; } // If we are authenticated using an app password skip all this if ($this->session->exists('app_password')) { return false; } // First check if the session tells us we should do 2FA (99% case) if (!$this->session->exists(self::SESSION_UID_KEY)) { // Check if the session tells us it is 2FA authenticated already if ($this->session->exists(self::SESSION_UID_DONE) && $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) { return false; } /* * If the session is expired check if we are not logged in by a token * that still needs 2FA auth */ try { $sessionId = $this->session->getId(); $token = $this->tokenProvider->getToken($sessionId); $tokenId = $token->getId(); $tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa'); if (!in_array($tokenId, $tokensNeeding2FA, true)) { $this->session->set(self::SESSION_UID_DONE, $user->getUID()); return false; } } catch (InvalidTokenException $e) { } } if (!$this->isTwoFactorAuthenticated($user)) { // There is no second factor any more -> let the user pass // This prevents infinite redirect loops when a user is about // to solve the 2FA challenge, and the provider app is // disabled the same time $this->session->remove(self::SESSION_UID_KEY); $keys = $this->config->getUserKeys($user->getUID(), 'login_token_2fa'); foreach ($keys as $key) { $this->config->deleteUserValue($user->getUID(), 'login_token_2fa', $key); } return false; } return true; } /** * Prepare the 2FA login * * @param IUser $user * @param boolean $rememberMe */ public function prepareTwoFactorLogin(IUser $user, $rememberMe) { $this->session->set(self::SESSION_UID_KEY, $user->getUID()); $this->session->set(self::REMEMBER_LOGIN, $rememberMe); $id = $this->session->getId(); $token = $this->tokenProvider->getToken($id); $this->config->setUserValue($user->getUID(), 'login_token_2fa', $token->getId(), $this->timeFactory->getTime()); } } private/Authentication/Exceptions/InvalidTokenException.php 0000604 00000001545 15247130452 0020301 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Exceptions; use Exception; class InvalidTokenException extends Exception { } private/Authentication/Exceptions/LoginRequiredException.php 0000604 00000001546 15247130452 0020464 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Exceptions; use Exception; class LoginRequiredException extends Exception { } private/Authentication/Exceptions/PasswordLoginForbiddenException.php 0000604 00000001556 15247130452 0022324 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Exceptions; use Exception; class PasswordLoginForbiddenException extends Exception { } private/Authentication/Exceptions/TwoFactorAuthRequiredException.php 0000604 00000001556 15247130452 0022147 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Exceptions; use Exception; class TwoFactorAuthRequiredException extends Exception { } private/Authentication/Exceptions/PasswordlessTokenException.php 0000604 00000001551 15247130452 0021401 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Exceptions; use Exception; class PasswordlessTokenException extends Exception { } private/Authentication/Exceptions/UserAlreadyLoggedInException.php 0000604 00000001554 15247130452 0021543 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Exceptions; use Exception; class UserAlreadyLoggedInException extends Exception { } private/Authentication/Token/DefaultToken.php 0000604 00000006567 15247130452 0015370 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Token; use OCP\AppFramework\Db\Entity; /** * @method void setId(int $id) * @method void setUid(string $uid); * @method void setLoginName(string $loginName) * @method void setPassword(string $password) * @method void setName(string $name) * @method string getName() * @method void setToken(string $token) * @method string getToken() * @method void setType(string $type) * @method int getType() * @method void setRemember(int $remember) * @method int getRemember() * @method void setLastActivity(int $lastActivity) * @method int getLastActivity() */ class DefaultToken extends Entity implements IToken { /** * @var string user UID */ protected $uid; /** * @var string login name used for generating the token */ protected $loginName; /** * @var string encrypted user password */ protected $password; /** * @var string token name (e.g. browser/OS) */ protected $name; /** * @var string */ protected $token; /** * @var int */ protected $type; /** * @var int */ protected $remember; /** * @var int */ protected $lastActivity; /** * @var int */ protected $lastCheck; /** * @var string */ protected $scope; public function __construct() { $this->addType('type', 'int'); $this->addType('lastActivity', 'int'); $this->addType('lastCheck', 'int'); } public function getId() { return $this->id; } public function getUID() { return $this->uid; } /** * Get the login name used when generating the token * * @return string */ public function getLoginName() { return parent::getLoginName(); } /** * Get the (encrypted) login password * * @return string */ public function getPassword() { return parent::getPassword(); } public function jsonSerialize() { return [ 'id' => $this->id, 'name' => $this->name, 'lastActivity' => $this->lastActivity, 'type' => $this->type, 'scope' => $this->getScopeAsArray() ]; } /** * Get the timestamp of the last password check * * @return int */ public function getLastCheck() { return parent::getLastCheck(); } /** * Get the timestamp of the last password check * * @param int $time */ public function setLastCheck($time) { return parent::setLastCheck($time); } public function getScope() { return parent::getScope(); } public function getScopeAsArray() { $scope = json_decode($this->getScope(), true); if (!$scope) { return [ 'filesystem'=> true ]; } return $scope; } public function setScope($scope) { if (is_array($scope)) { parent::setScope(json_encode($scope)); } else { parent::setScope((string)$scope); } } } private/Authentication/Token/IToken.php 0000604 00000003627 15247130452 0014166 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Token; use JsonSerializable; interface IToken extends JsonSerializable { const TEMPORARY_TOKEN = 0; const PERMANENT_TOKEN = 1; const DO_NOT_REMEMBER = 0; const REMEMBER = 1; /** * Get the token ID * * @return int */ public function getId(); /** * Get the user UID * * @return string */ public function getUID(); /** * Get the login name used when generating the token * * @return string */ public function getLoginName(); /** * Get the (encrypted) login password * * @return string */ public function getPassword(); /** * Get the timestamp of the last password check * * @return int */ public function getLastCheck(); /** * Set the timestamp of the last password check * * @param int $time */ public function setLastCheck($time); /** * Get the authentication scope for this token * * @return string */ public function getScope(); /** * Get the authentication scope for this token * * @return array */ public function getScopeAsArray(); /** * Set the authentication scope for this token * * @param array $scope */ public function setScope($scope); } private/Authentication/Token/IProvider.php 0000604 00000006440 15247130452 0014674 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Token; use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; use OCP\IUser; interface IProvider { /** * Create and persist a new token * * @param string $token * @param string $uid * @param string $loginName * @param string|null $password * @param string $name * @param int $type token type * @param int $remember whether the session token should be used for remember-me * @return IToken */ public function generateToken($token, $uid, $loginName, $password, $name, $type = IToken::TEMPORARY_TOKEN, $remember = IToken::DO_NOT_REMEMBER); /** * Get a token by token id * * @param string $tokenId * @throws InvalidTokenException * @return IToken */ public function getToken($tokenId); /** * Get a token by token id * * @param string $tokenId * @throws InvalidTokenException * @return DefaultToken */ public function getTokenById($tokenId); /** * Duplicate an existing session token * * @param string $oldSessionId * @param string $sessionId * @throws InvalidTokenException */ public function renewSessionToken($oldSessionId, $sessionId); /** * Invalidate (delete) the given session token * * @param string $token */ public function invalidateToken($token); /** * Invalidate (delete) the given token * * @param IUser $user * @param int $id */ public function invalidateTokenById(IUser $user, $id); /** * Invalidate (delete) old session tokens */ public function invalidateOldTokens(); /** * Save the updated token * * @param IToken $token */ public function updateToken(IToken $token); /** * Update token activity timestamp * * @param IToken $token */ public function updateTokenActivity(IToken $token); /** * Get all token of a user * * The provider may limit the number of result rows in case of an abuse * where a high number of (session) tokens is generated * * @param IUser $user * @return IToken[] */ public function getTokenByUser(IUser $user); /** * Get the (unencrypted) password of the given token * * @param IToken $token * @param string $tokenId * @throws InvalidTokenException * @throws PasswordlessTokenException * @return string */ public function getPassword(IToken $token, $tokenId); /** * Encrypt and set the password of the given token * * @param IToken $token * @param string $tokenId * @param string $password * @throws InvalidTokenException */ public function setPassword(IToken $token, $tokenId, $password); } private/Authentication/Token/DefaultTokenProvider.php 0000604 00000020307 15247130452 0017067 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Christoph Wurst <christoph@winzerhof-wurst.at> * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Token; use Exception; use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; use OCP\ILogger; use OCP\IUser; use OCP\Security\ICrypto; class DefaultTokenProvider implements IProvider { /** @var DefaultTokenMapper */ private $mapper; /** @var ICrypto */ private $crypto; /** @var IConfig */ private $config; /** @var ILogger $logger */ private $logger; /** @var ITimeFactory $time */ private $time; /** * @param DefaultTokenMapper $mapper * @param ICrypto $crypto * @param IConfig $config * @param ILogger $logger * @param ITimeFactory $time */ public function __construct(DefaultTokenMapper $mapper, ICrypto $crypto, IConfig $config, ILogger $logger, ITimeFactory $time) { $this->mapper = $mapper; $this->crypto = $crypto; $this->config = $config; $this->logger = $logger; $this->time = $time; } /** * Create and persist a new token * * @param string $token * @param string $uid * @param string $loginName * @param string|null $password * @param string $name * @param int $type token type * @param int $remember whether the session token should be used for remember-me * @return IToken */ public function generateToken($token, $uid, $loginName, $password, $name, $type = IToken::TEMPORARY_TOKEN, $remember = IToken::DO_NOT_REMEMBER) { $dbToken = new DefaultToken(); $dbToken->setUid($uid); $dbToken->setLoginName($loginName); if (!is_null($password)) { $dbToken->setPassword($this->encryptPassword($password, $token)); } $dbToken->setName($name); $dbToken->setToken($this->hashToken($token)); $dbToken->setType($type); $dbToken->setRemember($remember); $dbToken->setLastActivity($this->time->getTime()); $this->mapper->insert($dbToken); return $dbToken; } /** * Save the updated token * * @param IToken $token * @throws InvalidTokenException */ public function updateToken(IToken $token) { if (!($token instanceof DefaultToken)) { throw new InvalidTokenException(); } $this->mapper->update($token); } /** * Update token activity timestamp * * @throws InvalidTokenException * @param IToken $token */ public function updateTokenActivity(IToken $token) { if (!($token instanceof DefaultToken)) { throw new InvalidTokenException(); } /** @var DefaultToken $token */ $now = $this->time->getTime(); if ($token->getLastActivity() < ($now - 60)) { // Update token only once per minute $token->setLastActivity($now); $this->mapper->update($token); } } /** * Get all token of a user * * The provider may limit the number of result rows in case of an abuse * where a high number of (session) tokens is generated * * @param IUser $user * @return IToken[] */ public function getTokenByUser(IUser $user) { return $this->mapper->getTokenByUser($user); } /** * Get a token by token * * @param string $tokenId * @throws InvalidTokenException * @return DefaultToken */ public function getToken($tokenId) { try { return $this->mapper->getToken($this->hashToken($tokenId)); } catch (DoesNotExistException $ex) { throw new InvalidTokenException(); } } /** * Get a token by token id * * @param string $tokenId * @throws InvalidTokenException * @return DefaultToken */ public function getTokenById($tokenId) { try { return $this->mapper->getTokenById($tokenId); } catch (DoesNotExistException $ex) { throw new InvalidTokenException(); } } /** * @param string $oldSessionId * @param string $sessionId * @throws InvalidTokenException */ public function renewSessionToken($oldSessionId, $sessionId) { $token = $this->getToken($oldSessionId); $newToken = new DefaultToken(); $newToken->setUid($token->getUID()); $newToken->setLoginName($token->getLoginName()); if (!is_null($token->getPassword())) { $password = $this->decryptPassword($token->getPassword(), $oldSessionId); $newToken->setPassword($this->encryptPassword($password, $sessionId)); } $newToken->setName($token->getName()); $newToken->setToken($this->hashToken($sessionId)); $newToken->setType(IToken::TEMPORARY_TOKEN); $newToken->setRemember($token->getRemember()); $newToken->setLastActivity($this->time->getTime()); $this->mapper->insert($newToken); } /** * @param IToken $savedToken * @param string $tokenId session token * @throws InvalidTokenException * @throws PasswordlessTokenException * @return string */ public function getPassword(IToken $savedToken, $tokenId) { $password = $savedToken->getPassword(); if (is_null($password)) { throw new PasswordlessTokenException(); } return $this->decryptPassword($password, $tokenId); } /** * Encrypt and set the password of the given token * * @param IToken $token * @param string $tokenId * @param string $password * @throws InvalidTokenException */ public function setPassword(IToken $token, $tokenId, $password) { if (!($token instanceof DefaultToken)) { throw new InvalidTokenException(); } /** @var DefaultToken $token */ $token->setPassword($this->encryptPassword($password, $tokenId)); $this->mapper->update($token); } /** * Invalidate (delete) the given session token * * @param string $token */ public function invalidateToken($token) { $this->mapper->invalidate($this->hashToken($token)); } /** * Invalidate (delete) the given token * * @param IUser $user * @param int $id */ public function invalidateTokenById(IUser $user, $id) { $this->mapper->deleteById($user, $id); } /** * Invalidate (delete) old session tokens */ public function invalidateOldTokens() { $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24); $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']); $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER); $rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); $this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']); $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER); } /** * @param string $token * @return string */ private function hashToken($token) { $secret = $this->config->getSystemValue('secret'); return hash('sha512', $token . $secret); } /** * Encrypt the given password * * The token is used as key * * @param string $password * @param string $token * @return string encrypted password */ private function encryptPassword($password, $token) { $secret = $this->config->getSystemValue('secret'); return $this->crypto->encrypt($password, $token . $secret); } /** * Decrypt the given password * * The token is used as key * * @param string $password * @param string $token * @throws InvalidTokenException * @return string the decrypted key */ private function decryptPassword($password, $token) { $secret = $this->config->getSystemValue('secret'); try { return $this->crypto->decrypt($password, $token . $secret); } catch (Exception $ex) { // Delete the invalid token $this->invalidateToken($token); throw new InvalidTokenException(); } } } private/Authentication/Token/DefaultTokenMapper.php 0000604 00000011407 15247130452 0016522 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Token; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\Mapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; class DefaultTokenMapper extends Mapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'authtoken'); } /** * Invalidate (delete) a given token * * @param string $token */ public function invalidate($token) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') ->where($qb->expr()->eq('token', $qb->createParameter('token'))) ->setParameter('token', $token) ->execute(); } /** * @param int $olderThan * @param int $remember */ public function invalidateOld($olderThan, $remember = IToken::DO_NOT_REMEMBER) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') ->where($qb->expr()->lt('last_activity', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT))) ->execute(); } /** * Get the user UID for the given token * * @param string $token * @throws DoesNotExistException * @return DefaultToken */ public function getToken($token) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check', 'scope') ->from('authtoken') ->where($qb->expr()->eq('token', $qb->createNamedParameter($token))) ->execute(); $data = $result->fetch(); $result->closeCursor(); if ($data === false) { throw new DoesNotExistException('token does not exist'); } ; return DefaultToken::fromRow($data); } /** * Get the token for $id * * @param string $id * @throws DoesNotExistException * @return DefaultToken */ public function getTokenById($id) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check', 'scope') ->from('authtoken') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->execute(); $data = $result->fetch(); $result->closeCursor(); if ($data === false) { throw new DoesNotExistException('token does not exist'); }; return DefaultToken::fromRow($data); } /** * Get all token of a user * * The provider may limit the number of result rows in case of an abuse * where a high number of (session) tokens is generated * * @param IUser $user * @return DefaultToken[] */ public function getTokenByUser(IUser $user) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check', 'scope') ->from('authtoken') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) ->setMaxResults(1000); $result = $qb->execute(); $data = $result->fetchAll(); $result->closeCursor(); $entities = array_map(function ($row) { return DefaultToken::fromRow($row); }, $data); return $entities; } /** * @param IUser $user * @param int $id */ public function deleteById(IUser $user, $id) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))); $qb->execute(); } /** * delete all auth token which belong to a specific client if the client was deleted * * @param string $name */ public function deleteByName($name) { $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') ->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR)); $qb->execute(); } } private/Authentication/Token/DefaultTokenCleanupJob.php 0000604 00000002042 15247130452 0017313 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Authentication\Token; use OC; use OC\BackgroundJob\Job; class DefaultTokenCleanupJob extends Job { protected function run($argument) { /* @var $provider IProvider */ $provider = OC::$server->query('OC\Authentication\Token\IProvider'); $provider->invalidateOldTokens(); } } private/Authentication/LoginCredentials/Store.php 0000604 00000007122 15247130452 0016231 0 ustar 00 <?php /** * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Authentication\LoginCredentials; use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Token\IProvider; use OCP\Authentication\Exceptions\CredentialsUnavailableException; use OCP\Authentication\LoginCredentials\ICredentials; use OCP\Authentication\LoginCredentials\IStore; use OCP\ILogger; use OCP\ISession; use OCP\Session\Exceptions\SessionNotAvailableException; use OCP\Util; class Store implements IStore { /** @var ISession */ private $session; /** @var ILogger */ private $logger; /** @var IProvider|null */ private $tokenProvider; /** * @param ISession $session * @param ILogger $logger * @param IProvider $tokenProvider */ public function __construct(ISession $session, ILogger $logger, IProvider $tokenProvider = null) { $this->session = $session; $this->logger = $logger; $this->tokenProvider = $tokenProvider; Util::connectHook('OC_User', 'post_login', $this, 'authenticate'); } /** * Hook listener on post login * * @param array $params */ public function authenticate(array $params) { $this->session->set('login_credentials', json_encode($params)); } /** * Replace the session implementation * * @param ISession $session */ public function setSession(ISession $session) { $this->session = $session; } /** * @since 12 * * @return ICredentials the login credentials of the current user * @throws CredentialsUnavailableException */ public function getLoginCredentials() { if (is_null($this->tokenProvider)) { throw new CredentialsUnavailableException(); } $trySession = false; try { $sessionId = $this->session->getId(); $token = $this->tokenProvider->getToken($sessionId); $uid = $token->getUID(); $user = $token->getLoginName(); $password = $this->tokenProvider->getPassword($token, $sessionId); return new Credentials($uid, $user, $password); } catch (SessionNotAvailableException $ex) { $this->logger->debug('could not get login credentials because session is unavailable', ['app' => 'core']); } catch (InvalidTokenException $ex) { $this->logger->debug('could not get login credentials because the token is invalid', ['app' => 'core']); $trySession = true; } catch (PasswordlessTokenException $ex) { $this->logger->debug('could not get login credentials because the token has no password', ['app' => 'core']); $trySession = true; } if ($trySession && $this->session->exists('login_credentials')) { $creds = json_decode($this->session->get('login_credentials')); return new Credentials($creds->uid, $creds->uid, $creds->password); } // If we reach this line, an exception was thrown. throw new CredentialsUnavailableException(); } } private/Authentication/LoginCredentials/Credentials.php 0000604 00000003163 15247130452 0017373 0 ustar 00 <?php /** * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Authentication\LoginCredentials; use OCP\Authentication\LoginCredentials\ICredentials; class Credentials implements ICredentials { /** @var string */ private $uid; /** @var string */ private $loginName; /** @var string */ private $password; /** * @param string $uid * @param string $loginName * @param string $password */ public function __construct($uid, $loginName, $password) { $this->uid = $uid; $this->loginName = $loginName; $this->password = $password; } /** * @return string */ public function getUID() { return $this->uid; } /** * @return string */ public function getLoginName() { return $this->loginName; } /** * @return string */ public function getPassword() { return $this->password; } } private/Share20/Manager.php 0000604 00000135153 15247130452 0011514 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share20; use OC\Cache\CappedMemoryCache; use OC\Files\Mount\MoveableMount; use OC\HintException; use OC\Share20\Exception\ProviderException; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountManager; use OCP\Files\Node; use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory; use OCP\Mail\IMailer; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; use OCP\Share\Exceptions\GenericShareException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; use OCP\Share\IProviderFactory; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\GenericEvent; use OCP\Share\IShareProvider; /** * This class is the communication hub for all sharing related operations. */ class Manager implements IManager { /** @var IProviderFactory */ private $factory; /** @var ILogger */ private $logger; /** @var IConfig */ private $config; /** @var ISecureRandom */ private $secureRandom; /** @var IHasher */ private $hasher; /** @var IMountManager */ private $mountManager; /** @var IGroupManager */ private $groupManager; /** @var IL10N */ private $l; /** @var IFactory */ private $l10nFactory; /** @var IUserManager */ private $userManager; /** @var IRootFolder */ private $rootFolder; /** @var CappedMemoryCache */ private $sharingDisabledForUsersCache; /** @var EventDispatcher */ private $eventDispatcher; /** @var LegacyHooks */ private $legacyHooks; /** @var IMailer */ private $mailer; /** @var IURLGenerator */ private $urlGenerator; /** @var \OC_Defaults */ private $defaults; /** * Manager constructor. * * @param ILogger $logger * @param IConfig $config * @param ISecureRandom $secureRandom * @param IHasher $hasher * @param IMountManager $mountManager * @param IGroupManager $groupManager * @param IL10N $l * @param IFactory $l10nFactory * @param IProviderFactory $factory * @param IUserManager $userManager * @param IRootFolder $rootFolder * @param EventDispatcher $eventDispatcher * @param IMailer $mailer * @param IURLGenerator $urlGenerator * @param \OC_Defaults $defaults */ public function __construct( ILogger $logger, IConfig $config, ISecureRandom $secureRandom, IHasher $hasher, IMountManager $mountManager, IGroupManager $groupManager, IL10N $l, IFactory $l10nFactory, IProviderFactory $factory, IUserManager $userManager, IRootFolder $rootFolder, EventDispatcher $eventDispatcher, IMailer $mailer, IURLGenerator $urlGenerator, \OC_Defaults $defaults ) { $this->logger = $logger; $this->config = $config; $this->secureRandom = $secureRandom; $this->hasher = $hasher; $this->mountManager = $mountManager; $this->groupManager = $groupManager; $this->l = $l; $this->l10nFactory = $l10nFactory; $this->factory = $factory; $this->userManager = $userManager; $this->rootFolder = $rootFolder; $this->eventDispatcher = $eventDispatcher; $this->sharingDisabledForUsersCache = new CappedMemoryCache(); $this->legacyHooks = new LegacyHooks($this->eventDispatcher); $this->mailer = $mailer; $this->urlGenerator = $urlGenerator; $this->defaults = $defaults; } /** * Convert from a full share id to a tuple (providerId, shareId) * * @param string $id * @return string[] */ private function splitFullId($id) { return explode(':', $id, 2); } /** * Verify if a password meets all requirements * * @param string $password * @throws \Exception */ protected function verifyPassword($password) { if ($password === null) { // No password is set, check if this is allowed. if ($this->shareApiLinkEnforcePassword()) { throw new \InvalidArgumentException('Passwords are enforced for link shares'); } return; } // Let others verify the password try { $event = new GenericEvent($password); $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event); } catch (HintException $e) { throw new \Exception($e->getHint()); } } /** * Check for generic requirements before creating a share * * @param \OCP\Share\IShare $share * @throws \InvalidArgumentException * @throws GenericShareException */ protected function generalCreateChecks(\OCP\Share\IShare $share) { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { // We expect a valid user as sharedWith for user shares if (!$this->userManager->userExists($share->getSharedWith())) { throw new \InvalidArgumentException('SharedWith is not a valid user'); } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { // We expect a valid group as sharedWith for group shares if (!$this->groupManager->groupExists($share->getSharedWith())) { throw new \InvalidArgumentException('SharedWith is not a valid group'); } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { if ($share->getSharedWith() !== null) { throw new \InvalidArgumentException('SharedWith should be empty'); } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { if ($share->getSharedWith() === null) { throw new \InvalidArgumentException('SharedWith should not be empty'); } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { if ($share->getSharedWith() === null) { throw new \InvalidArgumentException('SharedWith should not be empty'); } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) { $circle = \OCA\Circles\Api\Circles::detailsCircle($share->getSharedWith()); if ($circle === null) { throw new \InvalidArgumentException('SharedWith is not a valid circle'); } } else { // We can't handle other types yet throw new \InvalidArgumentException('unknown share type'); } // Verify the initiator of the share is set if ($share->getSharedBy() === null) { throw new \InvalidArgumentException('SharedBy should be set'); } // Cannot share with yourself if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() === $share->getSharedBy()) { throw new \InvalidArgumentException('Can\'t share with yourself'); } // The path should be set if ($share->getNode() === null) { throw new \InvalidArgumentException('Path should be set'); } // And it should be a file or a folder if (!($share->getNode() instanceof \OCP\Files\File) && !($share->getNode() instanceof \OCP\Files\Folder)) { throw new \InvalidArgumentException('Path should be either a file or a folder'); } // And you can't share your rootfolder if ($this->userManager->userExists($share->getSharedBy())) { $sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath(); } else { $sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath(); } if ($sharedPath === $share->getNode()->getPath()) { throw new \InvalidArgumentException('You can\'t share your root folder'); } // Check if we actually have share permissions if (!$share->getNode()->isShareable()) { $message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]); throw new GenericShareException($message_t, $message_t, 404); } // Permissions should be set if ($share->getPermissions() === null) { throw new \InvalidArgumentException('A share requires permissions'); } /* * Quick fix for #23536 * Non moveable mount points do not have update and delete permissions * while we 'most likely' do have that on the storage. */ $permissions = $share->getNode()->getPermissions(); $mount = $share->getNode()->getMountPoint(); if (!($mount instanceof MoveableMount)) { $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE; } // Check that we do not share with more permissions than we have if ($share->getPermissions() & ~$permissions) { $message_t = $this->l->t('Cannot increase permissions of %s', [$share->getNode()->getPath()]); throw new GenericShareException($message_t, $message_t, 404); } // Check that read permissions are always set // Link shares are allowed to have no read permissions to allow upload to hidden folders $noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL; if (!$noReadPermissionRequired && ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) { throw new \InvalidArgumentException('Shares need at least read permissions'); } if ($share->getNode() instanceof \OCP\Files\File) { if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) { $message_t = $this->l->t('Files can\'t be shared with delete permissions'); throw new GenericShareException($message_t); } if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) { $message_t = $this->l->t('Files can\'t be shared with create permissions'); throw new GenericShareException($message_t); } } } /** * Validate if the expiration date fits the system settings * * @param \OCP\Share\IShare $share The share to validate the expiration date of * @return \OCP\Share\IShare The modified share object * @throws GenericShareException * @throws \InvalidArgumentException * @throws \Exception */ protected function validateExpirationDate(\OCP\Share\IShare $share) { $expirationDate = $share->getExpirationDate(); if ($expirationDate !== null) { //Make sure the expiration date is a date $expirationDate->setTime(0, 0, 0); $date = new \DateTime(); $date->setTime(0, 0, 0); if ($date >= $expirationDate) { $message = $this->l->t('Expiration date is in the past'); throw new GenericShareException($message, $message, 404); } } // If expiredate is empty set a default one if there is a default $fullId = null; try { $fullId = $share->getFullId(); } catch (\UnexpectedValueException $e) { // This is a new share } if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) { $expirationDate = new \DateTime(); $expirationDate->setTime(0,0,0); $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D')); } // If we enforce the expiration date check that is does not exceed if ($this->shareApiLinkDefaultExpireDateEnforced()) { if ($expirationDate === null) { throw new \InvalidArgumentException('Expiration date is enforced'); } $date = new \DateTime(); $date->setTime(0, 0, 0); $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D')); if ($date < $expirationDate) { $message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]); throw new GenericShareException($message, $message, 404); } } $accepted = true; $message = ''; \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [ 'expirationDate' => &$expirationDate, 'accepted' => &$accepted, 'message' => &$message, 'passwordSet' => $share->getPassword() !== null, ]); if (!$accepted) { throw new \Exception($message); } $share->setExpirationDate($expirationDate); return $share; } /** * Check for pre share requirements for user shares * * @param \OCP\Share\IShare $share * @throws \Exception */ protected function userCreateChecks(\OCP\Share\IShare $share) { // Check if we can share with group members only if ($this->shareWithGroupMembersOnly()) { $sharedBy = $this->userManager->get($share->getSharedBy()); $sharedWith = $this->userManager->get($share->getSharedWith()); // Verify we can share with this user $groups = array_intersect( $this->groupManager->getUserGroupIds($sharedBy), $this->groupManager->getUserGroupIds($sharedWith) ); if (empty($groups)) { throw new \Exception('Only sharing with group members is allowed'); } } /* * TODO: Could be costly, fix * * Also this is not what we want in the future.. then we want to squash identical shares. */ $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER); $existingShares = $provider->getSharesByPath($share->getNode()); foreach($existingShares as $existingShare) { // Ignore if it is the same share try { if ($existingShare->getFullId() === $share->getFullId()) { continue; } } catch (\UnexpectedValueException $e) { //Shares are not identical } // Identical share already existst if ($existingShare->getSharedWith() === $share->getSharedWith()) { throw new \Exception('Path already shared with this user'); } // The share is already shared with this user via a group share if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $group = $this->groupManager->get($existingShare->getSharedWith()); if (!is_null($group)) { $user = $this->userManager->get($share->getSharedWith()); if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) { throw new \Exception('Path already shared with this user'); } } } } } /** * Check for pre share requirements for group shares * * @param \OCP\Share\IShare $share * @throws \Exception */ protected function groupCreateChecks(\OCP\Share\IShare $share) { // Verify group shares are allowed if (!$this->allowGroupSharing()) { throw new \Exception('Group sharing is now allowed'); } // Verify if the user can share with this group if ($this->shareWithGroupMembersOnly()) { $sharedBy = $this->userManager->get($share->getSharedBy()); $sharedWith = $this->groupManager->get($share->getSharedWith()); if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) { throw new \Exception('Only sharing within your own groups is allowed'); } } /* * TODO: Could be costly, fix * * Also this is not what we want in the future.. then we want to squash identical shares. */ $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); $existingShares = $provider->getSharesByPath($share->getNode()); foreach($existingShares as $existingShare) { try { if ($existingShare->getFullId() === $share->getFullId()) { continue; } } catch (\UnexpectedValueException $e) { //It is a new share so just continue } if ($existingShare->getSharedWith() === $share->getSharedWith()) { throw new \Exception('Path already shared with this group'); } } } /** * Check for pre share requirements for link shares * * @param \OCP\Share\IShare $share * @throws \Exception */ protected function linkCreateChecks(\OCP\Share\IShare $share) { // Are link shares allowed? if (!$this->shareApiAllowLinks()) { throw new \Exception('Link sharing not allowed'); } // Link shares by definition can't have share permissions if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) { throw new \InvalidArgumentException('Link shares can\'t have reshare permissions'); } // Check if public upload is allowed if (!$this->shareApiLinkAllowPublicUpload() && ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) { throw new \InvalidArgumentException('Public upload not allowed'); } } /** * To make sure we don't get invisible link shares we set the parent * of a link if it is a reshare. This is a quick word around * until we can properly display multiple link shares in the UI * * See: https://github.com/owncloud/core/issues/22295 * * FIXME: Remove once multiple link shares can be properly displayed * * @param \OCP\Share\IShare $share */ protected function setLinkParent(\OCP\Share\IShare $share) { // No sense in checking if the method is not there. if (method_exists($share, 'setParent')) { $storage = $share->getNode()->getStorage(); if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { /** @var \OCA\Files_Sharing\SharedStorage $storage */ $share->setParent($storage->getShareId()); } }; } /** * @param File|Folder $path */ protected function pathCreateChecks($path) { // Make sure that we do not share a path that contains a shared mountpoint if ($path instanceof \OCP\Files\Folder) { $mounts = $this->mountManager->findIn($path->getPath()); foreach($mounts as $mount) { if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { throw new \InvalidArgumentException('Path contains files shared with you'); } } } } /** * Check if the user that is sharing can actually share * * @param \OCP\Share\IShare $share * @throws \Exception */ protected function canShare(\OCP\Share\IShare $share) { if (!$this->shareApiEnabled()) { throw new \Exception('The share API is disabled'); } if ($this->sharingDisabledForUser($share->getSharedBy())) { throw new \Exception('You are not allowed to share'); } } /** * Share a path * * @param \OCP\Share\IShare $share * @return Share The share object * @throws \Exception * * TODO: handle link share permissions or check them */ public function createShare(\OCP\Share\IShare $share) { $this->canShare($share); $this->generalCreateChecks($share); // Verify if there are any issues with the path $this->pathCreateChecks($share->getNode()); /* * On creation of a share the owner is always the owner of the path * Except for mounted federated shares. */ $storage = $share->getNode()->getStorage(); if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { $parent = $share->getNode()->getParent(); while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { $parent = $parent->getParent(); } $share->setShareOwner($parent->getOwner()->getUID()); } else { $share->setShareOwner($share->getNode()->getOwner()->getUID()); } //Verify share type if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { $this->userCreateChecks($share); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $this->groupCreateChecks($share); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { $this->linkCreateChecks($share); $this->setLinkParent($share); /* * For now ignore a set token. */ $share->setToken( $this->secureRandom->generate( \OC\Share\Constants::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER. \OCP\Security\ISecureRandom::CHAR_UPPER. \OCP\Security\ISecureRandom::CHAR_DIGITS ) ); //Verify the expiration date $this->validateExpirationDate($share); //Verify the password $this->verifyPassword($share->getPassword()); // If a password is set. Hash it! if ($share->getPassword() !== null) { $share->setPassword($this->hasher->hash($share->getPassword())); } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { $share->setToken( $this->secureRandom->generate( \OC\Share\Constants::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER. \OCP\Security\ISecureRandom::CHAR_UPPER. \OCP\Security\ISecureRandom::CHAR_DIGITS ) ); } // Cannot share with the owner if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() === $share->getShareOwner()) { throw new \InvalidArgumentException('Can\'t share with the share owner'); } // Generate the target $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName(); $target = \OC\Files\Filesystem::normalizePath($target); $share->setTarget($target); // Pre share hook $run = true; $error = ''; $preHookData = [ 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', 'itemSource' => $share->getNode()->getId(), 'shareType' => $share->getShareType(), 'uidOwner' => $share->getSharedBy(), 'permissions' => $share->getPermissions(), 'fileSource' => $share->getNode()->getId(), 'expiration' => $share->getExpirationDate(), 'token' => $share->getToken(), 'itemTarget' => $share->getTarget(), 'shareWith' => $share->getSharedWith(), 'run' => &$run, 'error' => &$error, ]; \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData); if ($run === false) { throw new \Exception($error); } $oldShare = $share; $provider = $this->factory->getProviderForType($share->getShareType()); $share = $provider->create($share); //reuse the node we already have $share->setNode($oldShare->getNode()); // Post share hook $postHookData = [ 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', 'itemSource' => $share->getNode()->getId(), 'shareType' => $share->getShareType(), 'uidOwner' => $share->getSharedBy(), 'permissions' => $share->getPermissions(), 'fileSource' => $share->getNode()->getId(), 'expiration' => $share->getExpirationDate(), 'token' => $share->getToken(), 'id' => $share->getId(), 'shareWith' => $share->getSharedWith(), 'itemTarget' => $share->getTarget(), 'fileTarget' => $share->getTarget(), ]; \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData); if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { $user = $this->userManager->get($share->getSharedWith()); if ($user !== null) { $emailAddress = $user->getEMailAddress(); if ($emailAddress !== null && $emailAddress !== '') { $userLang = $this->config->getUserValue($share->getSharedWith(), 'core', 'lang', null); $l = $this->l10nFactory->get('lib', $userLang); $this->sendMailNotification( $l, $share->getNode()->getName(), $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', [ 'fileid' => $share->getNode()->getId() ]), $share->getSharedBy(), $emailAddress, $share->getExpirationDate() ); $this->logger->debug('Send share notification to ' . $emailAddress . ' for share with ID ' . $share->getId(), ['app' => 'share']); } else { $this->logger->debug('Share notification not send to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']); } } else { $this->logger->debug('Share notification not send to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']); } } return $share; } /** * @param IL10N $l Language of the recipient * @param string $filename file/folder name * @param string $link link to the file/folder * @param string $initiator user ID of share sender * @param string $shareWith email address of share receiver * @param \DateTime|null $expiration * @throws \Exception If mail couldn't be sent */ protected function sendMailNotification(IL10N $l, $filename, $link, $initiator, $shareWith, \DateTime $expiration = null) { $initiatorUser = $this->userManager->get($initiator); $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; $subject = $l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)); $message = $this->mailer->createMessage(); $emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [ 'filename' => $filename, 'link' => $link, 'initiator' => $initiatorDisplayName, 'expiration' => $expiration, 'shareWith' => $shareWith, ]); $emailTemplate->addHeader(); $emailTemplate->addHeading($l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false); $text = $l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]); $emailTemplate->addBodyText( $text . ' ' . $l->t('Click the button below to open it.'), $text ); $emailTemplate->addBodyButton( $l->t('Open »%s«', [$filename]), $link ); $message->setTo([$shareWith]); // The "From" contains the sharers name $instanceName = $this->defaults->getName(); $senderName = $l->t( '%s via %s', [ $initiatorDisplayName, $instanceName ] ); $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); // The "Reply-To" is set to the sharer if an mail address is configured // also the default footer contains a "Do not reply" which needs to be adjusted. $initiatorEmail = $initiatorUser->getEMailAddress(); if($initiatorEmail !== null) { $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); } else { $emailTemplate->addFooter(); } $message->setSubject($subject); $message->setPlainBody($emailTemplate->renderText()); $message->setHtmlBody($emailTemplate->renderHtml()); $this->mailer->send($message); } /** * Update a share * * @param \OCP\Share\IShare $share * @return \OCP\Share\IShare The share object * @throws \InvalidArgumentException */ public function updateShare(\OCP\Share\IShare $share) { $expirationDateUpdated = false; $this->canShare($share); try { $originalShare = $this->getShareById($share->getFullId()); } catch (\UnexpectedValueException $e) { throw new \InvalidArgumentException('Share does not have a full id'); } // We can't change the share type! if ($share->getShareType() !== $originalShare->getShareType()) { throw new \InvalidArgumentException('Can\'t change share type'); } // We can only change the recipient on user shares if ($share->getSharedWith() !== $originalShare->getSharedWith() && $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) { throw new \InvalidArgumentException('Can only update recipient on user shares'); } // Cannot share with the owner if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() === $share->getShareOwner()) { throw new \InvalidArgumentException('Can\'t share with the share owner'); } $this->generalCreateChecks($share); if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { $this->userCreateChecks($share); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $this->groupCreateChecks($share); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { $this->linkCreateChecks($share); $this->updateSharePasswordIfNeeded($share, $originalShare); if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { //Verify the expiration date $this->validateExpirationDate($share); $expirationDateUpdated = true; } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { $plainTextPassword = $share->getPassword(); if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) { $plainTextPassword = null; } } $this->pathCreateChecks($share->getNode()); // Now update the share! $provider = $this->factory->getProviderForType($share->getShareType()); if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { $share = $provider->update($share, $plainTextPassword); } else { $share = $provider->update($share); } if ($expirationDateUpdated === true) { \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [ 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', 'itemSource' => $share->getNode()->getId(), 'date' => $share->getExpirationDate(), 'uidOwner' => $share->getSharedBy(), ]); } if ($share->getPassword() !== $originalShare->getPassword()) { \OC_Hook::emit('OCP\Share', 'post_update_password', [ 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', 'itemSource' => $share->getNode()->getId(), 'uidOwner' => $share->getSharedBy(), 'token' => $share->getToken(), 'disabled' => is_null($share->getPassword()), ]); } if ($share->getPermissions() !== $originalShare->getPermissions()) { if ($this->userManager->userExists($share->getShareOwner())) { $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); } else { $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); } \OC_Hook::emit('OCP\Share', 'post_update_permissions', array( 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', 'itemSource' => $share->getNode()->getId(), 'shareType' => $share->getShareType(), 'shareWith' => $share->getSharedWith(), 'uidOwner' => $share->getSharedBy(), 'permissions' => $share->getPermissions(), 'path' => $userFolder->getRelativePath($share->getNode()->getPath()), )); } return $share; } /** * Updates the password of the given share if it is not the same as the * password of the original share. * * @param \OCP\Share\IShare $share the share to update its password. * @param \OCP\Share\IShare $originalShare the original share to compare its * password with. * @return boolean whether the password was updated or not. */ private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) { // Password updated. if ($share->getPassword() !== $originalShare->getPassword()) { //Verify the password $this->verifyPassword($share->getPassword()); // If a password is set. Hash it! if ($share->getPassword() !== null) { $share->setPassword($this->hasher->hash($share->getPassword())); return true; } } return false; } /** * Delete all the children of this share * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in * * @param \OCP\Share\IShare $share * @return \OCP\Share\IShare[] List of deleted shares */ protected function deleteChildren(\OCP\Share\IShare $share) { $deletedShares = []; $provider = $this->factory->getProviderForType($share->getShareType()); foreach ($provider->getChildren($share) as $child) { $deletedChildren = $this->deleteChildren($child); $deletedShares = array_merge($deletedShares, $deletedChildren); $provider->delete($child); $deletedShares[] = $child; } return $deletedShares; } /** * Delete a share * * @param \OCP\Share\IShare $share * @throws ShareNotFound * @throws \InvalidArgumentException */ public function deleteShare(\OCP\Share\IShare $share) { try { $share->getFullId(); } catch (\UnexpectedValueException $e) { throw new \InvalidArgumentException('Share does not have a full id'); } $event = new GenericEvent($share); $this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event); // Get all children and delete them as well $deletedShares = $this->deleteChildren($share); // Do the actual delete $provider = $this->factory->getProviderForType($share->getShareType()); $provider->delete($share); // All the deleted shares caused by this delete $deletedShares[] = $share; // Emit post hook $event->setArgument('deletedShares', $deletedShares); $this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event); } /** * Unshare a file as the recipient. * This can be different from a regular delete for example when one of * the users in a groups deletes that share. But the provider should * handle this. * * @param \OCP\Share\IShare $share * @param string $recipientId */ public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) { list($providerId, ) = $this->splitFullId($share->getFullId()); $provider = $this->factory->getProvider($providerId); $provider->deleteFromSelf($share, $recipientId); } /** * @inheritdoc */ public function moveShare(\OCP\Share\IShare $share, $recipientId) { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { throw new \InvalidArgumentException('Can\'t change target of link share'); } if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) { throw new \InvalidArgumentException('Invalid recipient'); } if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $sharedWith = $this->groupManager->get($share->getSharedWith()); if (is_null($sharedWith)) { throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist'); } $recipient = $this->userManager->get($recipientId); if (!$sharedWith->inGroup($recipient)) { throw new \InvalidArgumentException('Invalid recipient'); } } list($providerId, ) = $this->splitFullId($share->getFullId()); $provider = $this->factory->getProvider($providerId); $provider->move($share, $recipientId); } public function getSharesInFolder($userId, Folder $node, $reshares = false) { $providers = $this->factory->getAllProviders(); return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) { $newShares = $provider->getSharesInFolder($userId, $node, $reshares); foreach ($newShares as $fid => $data) { if (!isset($shares[$fid])) { $shares[$fid] = []; } $shares[$fid] = array_merge($shares[$fid], $data); } return $shares; }, []); } /** * @inheritdoc */ public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) { if ($path !== null && !($path instanceof \OCP\Files\File) && !($path instanceof \OCP\Files\Folder)) { throw new \InvalidArgumentException('invalid path'); } try { $provider = $this->factory->getProviderForType($shareType); } catch (ProviderException $e) { return []; } $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); /* * Work around so we don't return expired shares but still follow * proper pagination. */ $shares2 = []; while(true) { $added = 0; foreach ($shares as $share) { try { $this->checkExpireDate($share); } catch (ShareNotFound $e) { //Ignore since this basically means the share is deleted continue; } $added++; $shares2[] = $share; if (count($shares2) === $limit) { break; } } if (count($shares2) === $limit) { break; } // If there was no limit on the select we are done if ($limit === -1) { break; } $offset += $added; // Fetch again $limit shares $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); // No more shares means we are done if (empty($shares)) { break; } } $shares = $shares2; return $shares; } /** * @inheritdoc */ public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) { try { $provider = $this->factory->getProviderForType($shareType); } catch (ProviderException $e) { return []; } $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset); // remove all shares which are already expired foreach ($shares as $key => $share) { try { $this->checkExpireDate($share); } catch (ShareNotFound $e) { unset($shares[$key]); } } return $shares; } /** * @inheritdoc */ public function getShareById($id, $recipient = null) { if ($id === null) { throw new ShareNotFound(); } list($providerId, $id) = $this->splitFullId($id); try { $provider = $this->factory->getProvider($providerId); } catch (ProviderException $e) { throw new ShareNotFound(); } $share = $provider->getShareById($id, $recipient); $this->checkExpireDate($share); return $share; } /** * Get all the shares for a given path * * @param \OCP\Files\Node $path * @param int $page * @param int $perPage * * @return Share[] */ public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) { return []; } /** * Get the share by token possible with password * * @param string $token * @return Share * * @throws ShareNotFound */ public function getShareByToken($token) { $share = null; try { if($this->shareApiAllowLinks()) { $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK); $share = $provider->getShareByToken($token); } } catch (ProviderException $e) { } catch (ShareNotFound $e) { } // If it is not a link share try to fetch a federated share by token if ($share === null) { try { $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE); $share = $provider->getShareByToken($token); } catch (ProviderException $e) { } catch (ShareNotFound $e) { } } // If it is not a link share try to fetch a mail share by token if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { try { $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL); $share = $provider->getShareByToken($token); } catch (ProviderException $e) { } catch (ShareNotFound $e) { } } if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) { try { $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_CIRCLE); $share = $provider->getShareByToken($token); } catch (ProviderException $e) { } catch (ShareNotFound $e) { } } if ($share === null) { throw new ShareNotFound($this->l->t('The requested share does not exist anymore')); } $this->checkExpireDate($share); /* * Reduce the permissions for link shares if public upload is not enabled */ if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && !$this->shareApiLinkAllowPublicUpload()) { $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE)); } return $share; } protected function checkExpireDate($share) { if ($share->getExpirationDate() !== null && $share->getExpirationDate() <= new \DateTime()) { $this->deleteShare($share); throw new ShareNotFound($this->l->t('The requested share does not exist anymore')); } } /** * Verify the password of a public share * * @param \OCP\Share\IShare $share * @param string $password * @return bool */ public function checkPassword(\OCP\Share\IShare $share, $password) { $passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK || $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL; if (!$passwordProtected) { //TODO maybe exception? return false; } if ($password === null || $share->getPassword() === null) { return false; } $newHash = ''; if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) { return false; } if (!empty($newHash)) { $share->setPassword($newHash); $provider = $this->factory->getProviderForType($share->getShareType()); $provider->update($share); } return true; } /** * @inheritdoc */ public function userDeleted($uid) { $types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL]; foreach ($types as $type) { try { $provider = $this->factory->getProviderForType($type); } catch (ProviderException $e) { continue; } $provider->userDeleted($uid, $type); } } /** * @inheritdoc */ public function groupDeleted($gid) { $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); $provider->groupDeleted($gid); } /** * @inheritdoc */ public function userDeletedFromGroup($uid, $gid) { $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); $provider->userDeletedFromGroup($uid, $gid); } /** * Get access list to a path. This means * all the users that can access a given path. * * Consider: * -root * |-folder1 (23) * |-folder2 (32) * |-fileA (42) * * fileA is shared with user1 and user1@server1 * folder2 is shared with group2 (user4 is a member of group2) * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2 * * Then the access list to '/folder1/folder2/fileA' with $currentAccess is: * [ * users => [ * 'user1' => ['node_id' => 42, 'node_path' => '/fileA'], * 'user4' => ['node_id' => 32, 'node_path' => '/folder2'], * 'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'], * ], * remote => [ * 'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'], * 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'], * ], * public => bool * mail => bool * ] * * The access list to '/folder1/folder2/fileA' **without** $currentAccess is: * [ * users => ['user1', 'user2', 'user4'], * remote => bool, * public => bool * mail => bool * ] * * This is required for encryption/activity * * @param \OCP\Files\Node $path * @param bool $recursive Should we check all parent folders as well * @param bool $currentAccess Should the user have currently access to the file * @return array */ public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) { $owner = $path->getOwner()->getUID(); if ($currentAccess) { $al = ['users' => [], 'remote' => [], 'public' => false]; } else { $al = ['users' => [], 'remote' => false, 'public' => false]; } if (!$this->userManager->userExists($owner)) { return $al; } //Get node for the owner $userFolder = $this->rootFolder->getUserFolder($owner); if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) { $path = $userFolder->getById($path->getId())[0]; } $providers = $this->factory->getAllProviders(); /** @var Node[] $nodes */ $nodes = []; if ($currentAccess) { $ownerPath = $path->getPath(); $ownerPath = explode('/', $ownerPath, 4); if (count($ownerPath) < 4) { $ownerPath = ''; } else { $ownerPath = $ownerPath[3]; } $al['users'][$owner] = [ 'node_id' => $path->getId(), 'node_path' => '/' . $ownerPath, ]; } else { $al['users'][] = $owner; } // Collect all the shares while ($path->getPath() !== $userFolder->getPath()) { $nodes[] = $path; if (!$recursive) { break; } $path = $path->getParent(); } foreach ($providers as $provider) { $tmp = $provider->getAccessList($nodes, $currentAccess); foreach ($tmp as $k => $v) { if (isset($al[$k])) { if (is_array($al[$k])) { $al[$k] = array_merge($al[$k], $v); } else { $al[$k] = $al[$k] || $v; } } else { $al[$k] = $v; } } } return $al; } /** * Create a new share * @return \OCP\Share\IShare; */ public function newShare() { return new \OC\Share20\Share($this->rootFolder, $this->userManager); } /** * Is the share API enabled * * @return bool */ public function shareApiEnabled() { return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes'; } /** * Is public link sharing enabled * * @return bool */ public function shareApiAllowLinks() { return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes'; } /** * Is password on public link requires * * @return bool */ public function shareApiLinkEnforcePassword() { return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes'; } /** * Is default expire date enabled * * @return bool */ public function shareApiLinkDefaultExpireDate() { return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes'; } /** * Is default expire date enforced *` * @return bool */ public function shareApiLinkDefaultExpireDateEnforced() { return $this->shareApiLinkDefaultExpireDate() && $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes'; } /** * Number of default expire days *shareApiLinkAllowPublicUpload * @return int */ public function shareApiLinkDefaultExpireDays() { return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'); } /** * Allow public upload on link shares * * @return bool */ public function shareApiLinkAllowPublicUpload() { return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes'; } /** * check if user can only share with group members * @return bool */ public function shareWithGroupMembersOnly() { return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; } /** * Check if users can share with groups * @return bool */ public function allowGroupSharing() { return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes'; } /** * Copied from \OC_Util::isSharingDisabledForUser * * TODO: Deprecate fuction from OC_Util * * @param string $userId * @return bool */ public function sharingDisabledForUser($userId) { if ($userId === null) { return false; } if (isset($this->sharingDisabledForUsersCache[$userId])) { return $this->sharingDisabledForUsersCache[$userId]; } if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); $excludedGroups = json_decode($groupsList); if (is_null($excludedGroups)) { $excludedGroups = explode(',', $groupsList); $newValue = json_encode($excludedGroups); $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); } $user = $this->userManager->get($userId); $usersGroups = $this->groupManager->getUserGroupIds($user); if (!empty($usersGroups)) { $remainingGroups = array_diff($usersGroups, $excludedGroups); // if the user is only in groups which are disabled for sharing then // sharing is also disabled for the user if (empty($remainingGroups)) { $this->sharingDisabledForUsersCache[$userId] = true; return true; } } } $this->sharingDisabledForUsersCache[$userId] = false; return false; } /** * @inheritdoc */ public function outgoingServer2ServerSharesAllowed() { return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes'; } /** * @inheritdoc */ public function shareProviderExists($shareType) { try { $this->factory->getProviderForType($shareType); } catch (ProviderException $e) { return false; } return true; } } private/Share20/ProviderFactory.php 0000604 00000017133 15247130452 0013261 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Lukas Reschke <lukas@statuscode.ch> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share20; use OC\CapabilitiesManager; use OC\GlobalScale\Config; use OCA\FederatedFileSharing\AddressHandler; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\Notifications; use OCA\FederatedFileSharing\TokenHandler; use OCA\ShareByMail\Settings\SettingsManager; use OCA\ShareByMail\ShareByMailProvider; use OCP\Defaults; use OCP\Share\IProviderFactory; use OC\Share20\Exception\ProviderException; use OCP\IServerContainer; /** * Class ProviderFactory * * @package OC\Share20 */ class ProviderFactory implements IProviderFactory { /** @var IServerContainer */ private $serverContainer; /** @var DefaultShareProvider */ private $defaultProvider = null; /** @var FederatedShareProvider */ private $federatedProvider = null; /** @var ShareByMailProvider */ private $shareByMailProvider; /** @var \OCA\Circles\ShareByCircleProvider */ private $shareByCircleProvider = null; /** @var bool */ private $circlesAreNotAvailable = false; /** * IProviderFactory constructor. * * @param IServerContainer $serverContainer */ public function __construct(IServerContainer $serverContainer) { $this->serverContainer = $serverContainer; } /** * Create the default share provider. * * @return DefaultShareProvider */ protected function defaultShareProvider() { if ($this->defaultProvider === null) { $this->defaultProvider = new DefaultShareProvider( $this->serverContainer->getDatabaseConnection(), $this->serverContainer->getUserManager(), $this->serverContainer->getGroupManager(), $this->serverContainer->getLazyRootFolder() ); } return $this->defaultProvider; } /** * Create the federated share provider * * @return FederatedShareProvider */ protected function federatedShareProvider() { if ($this->federatedProvider === null) { /* * Check if the app is enabled */ $appManager = $this->serverContainer->getAppManager(); if (!$appManager->isEnabledForUser('federatedfilesharing')) { return null; } /* * TODO: add factory to federated sharing app */ $l = $this->serverContainer->getL10N('federatedfilessharing'); $addressHandler = new AddressHandler( $this->serverContainer->getURLGenerator(), $l, $this->serverContainer->getCloudIdManager() ); $notifications = new Notifications( $addressHandler, $this->serverContainer->getHTTPClientService(), $this->serverContainer->query(\OCP\OCS\IDiscoveryService::class), $this->serverContainer->getJobList() ); $tokenHandler = new TokenHandler( $this->serverContainer->getSecureRandom() ); $this->federatedProvider = new FederatedShareProvider( $this->serverContainer->getDatabaseConnection(), $addressHandler, $notifications, $tokenHandler, $l, $this->serverContainer->getLogger(), $this->serverContainer->getLazyRootFolder(), $this->serverContainer->getConfig(), $this->serverContainer->getUserManager(), $this->serverContainer->getCloudIdManager(), $this->serverContainer->query(Config::class) ); } return $this->federatedProvider; } /** * Create the federated share provider * * @return ShareByMailProvider */ protected function getShareByMailProvider() { if ($this->shareByMailProvider === null) { /* * Check if the app is enabled */ $appManager = $this->serverContainer->getAppManager(); if (!$appManager->isEnabledForUser('sharebymail')) { return null; } $settingsManager = new SettingsManager($this->serverContainer->getConfig()); $this->shareByMailProvider = new ShareByMailProvider( $this->serverContainer->getDatabaseConnection(), $this->serverContainer->getSecureRandom(), $this->serverContainer->getUserManager(), $this->serverContainer->getLazyRootFolder(), $this->serverContainer->getL10N('sharebymail'), $this->serverContainer->getLogger(), $this->serverContainer->getMailer(), $this->serverContainer->getURLGenerator(), $this->serverContainer->getActivityManager(), $settingsManager, $this->serverContainer->query(Defaults::class), $this->serverContainer->getHasher(), $this->serverContainer->query(CapabilitiesManager::class) ); } return $this->shareByMailProvider; } /** * Create the circle share provider * * @return FederatedShareProvider */ protected function getShareByCircleProvider() { if ($this->circlesAreNotAvailable) { return null; } if (!$this->serverContainer->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider') ) { $this->circlesAreNotAvailable = true; return null; } if ($this->shareByCircleProvider === null) { $this->shareByCircleProvider = new \OCA\Circles\ShareByCircleProvider( $this->serverContainer->getDatabaseConnection(), $this->serverContainer->getSecureRandom(), $this->serverContainer->getUserManager(), $this->serverContainer->getLazyRootFolder(), $this->serverContainer->getL10N('circles'), $this->serverContainer->getLogger(), $this->serverContainer->getURLGenerator() ); } return $this->shareByCircleProvider; } /** * @inheritdoc */ public function getProvider($id) { $provider = null; if ($id === 'ocinternal') { $provider = $this->defaultShareProvider(); } else if ($id === 'ocFederatedSharing') { $provider = $this->federatedShareProvider(); } else if ($id === 'ocMailShare') { $provider = $this->getShareByMailProvider(); } else if ($id === 'ocCircleShare') { $provider = $this->getShareByCircleProvider(); } if ($provider === null) { throw new ProviderException('No provider with id .' . $id . ' found.'); } return $provider; } /** * @inheritdoc */ public function getProviderForType($shareType) { $provider = null; if ($shareType === \OCP\Share::SHARE_TYPE_USER || $shareType === \OCP\Share::SHARE_TYPE_GROUP || $shareType === \OCP\Share::SHARE_TYPE_LINK ) { $provider = $this->defaultShareProvider(); } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { $provider = $this->federatedShareProvider(); } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) { $provider = $this->getShareByMailProvider(); } else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) { $provider = $this->getShareByCircleProvider(); } if ($provider === null) { throw new ProviderException('No share provider for share type ' . $shareType); } return $provider; } public function getAllProviders() { $shares = [$this->defaultShareProvider(), $this->federatedShareProvider()]; $shareByMail = $this->getShareByMailProvider(); if ($shareByMail !== null) { $shares[] = $shareByMail; } $shareByCircle = $this->getShareByCircleProvider(); if ($shareByCircle !== null) { $shares[] = $shareByCircle; } return $shares; } } private/Share20/Hooks.php 0000604 00000002267 15247130452 0011224 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share20; class Hooks { public static function post_deleteUser($arguments) { \OC::$server->getShareManager()->userDeleted($arguments['uid']); } public static function post_deleteGroup($arguments) { \OC::$server->getShareManager()->groupDeleted($arguments['gid']); } public static function post_removeFromGroup($arguments) { \OC::$server->getShareManager()->userDeletedFromGroup($arguments['uid'], $arguments['gid']); } } private/Share20/ShareHelper.php 0000604 00000012757 15247130452 0012350 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Share20; use OCP\Files\InvalidPathException; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Share\IManager; use OCP\Share\IShareHelper; class ShareHelper implements IShareHelper { /** @var IManager */ private $shareManager; public function __construct(IManager $shareManager) { $this->shareManager = $shareManager; } /** * @param Node $node * @return array [ users => [Mapping $uid => $pathForUser], remotes => [Mapping $cloudId => $pathToMountRoot]] */ public function getPathsForAccessList(Node $node) { $result = [ 'users' => [], 'remotes' => [], ]; $accessList = $this->shareManager->getAccessList($node, true, true); if (!empty($accessList['users'])) { $result['users'] = $this->getPathsForUsers($node, $accessList['users']); } if (!empty($accessList['remote'])) { $result['remotes'] = $this->getPathsForRemotes($node, $accessList['remote']); } return $result; } /** * Sample: * $users = [ * 'test1' => ['node_id' => 16, 'node_path' => '/foo'], * 'test2' => ['node_id' => 23, 'node_path' => '/bar'], * 'test3' => ['node_id' => 42, 'node_path' => '/cat'], * 'test4' => ['node_id' => 48, 'node_path' => '/dog'], * ]; * * Node tree: * - SixTeen is the parent of TwentyThree * - TwentyThree is the parent of FortyTwo * - FortyEight does not exist * * $return = [ * 'test1' => '/foo/TwentyThree/FortyTwo', * 'test2' => '/bar/FortyTwo', * 'test3' => '/cat', * ], * * @param Node $node * @param array[] $users * @return array */ protected function getPathsForUsers(Node $node, array $users) { /** @var array[] $byId */ $byId = []; /** @var array[] $results */ $results = []; foreach ($users as $uid => $info) { if (!isset($byId[$info['node_id']])) { $byId[$info['node_id']] = []; } $byId[$info['node_id']][$uid] = $info['node_path']; } try { if (isset($byId[$node->getId()])) { foreach ($byId[$node->getId()] as $uid => $path) { $results[$uid] = $path; } unset($byId[$node->getId()]); } } catch (NotFoundException $e) { return $results; } catch (InvalidPathException $e) { return $results; } if (empty($byId)) { return $results; } $item = $node; $appendix = '/' . $node->getName(); while (!empty($byId)) { try { /** @var Node $item */ $item = $item->getParent(); if (!empty($byId[$item->getId()])) { foreach ($byId[$item->getId()] as $uid => $path) { $results[$uid] = $path . $appendix; } unset($byId[$item->getId()]); } $appendix = '/' . $item->getName() . $appendix; } catch (NotFoundException $e) { return $results; } catch (InvalidPathException $e) { return $results; } catch (NotPermittedException $e) { return $results; } } return $results; } /** * Sample: * $remotes = [ * 'test1' => ['node_id' => 16, 'token' => 't1'], * 'test2' => ['node_id' => 23, 'token' => 't2'], * 'test3' => ['node_id' => 42, 'token' => 't3'], * 'test4' => ['node_id' => 48, 'token' => 't4'], * ]; * * Node tree: * - SixTeen is the parent of TwentyThree * - TwentyThree is the parent of FortyTwo * - FortyEight does not exist * * $return = [ * 'test1' => ['token' => 't1', 'node_path' => '/SixTeen'], * 'test2' => ['token' => 't2', 'node_path' => '/SixTeen/TwentyThree'], * 'test3' => ['token' => 't3', 'node_path' => '/SixTeen/TwentyThree/FortyTwo'], * ], * * @param Node $node * @param array[] $remotes * @return array */ protected function getPathsForRemotes(Node $node, array $remotes) { /** @var array[] $byId */ $byId = []; /** @var array[] $results */ $results = []; foreach ($remotes as $cloudId => $info) { if (!isset($byId[$info['node_id']])) { $byId[$info['node_id']] = []; } $byId[$info['node_id']][$cloudId] = $info['token']; } $item = $node; while (!empty($byId)) { try { if (!empty($byId[$item->getId()])) { $path = $this->getMountedPath($item); foreach ($byId[$item->getId()] as $uid => $token) { $results[$uid] = [ 'node_path' => $path, 'token' => $token, ]; } unset($byId[$item->getId()]); } /** @var Node $item */ $item = $item->getParent(); } catch (NotFoundException $e) { return $results; } catch (InvalidPathException $e) { return $results; } catch (NotPermittedException $e) { return $results; } } return $results; } /** * @param Node $node * @return string */ protected function getMountedPath(Node $node) { $path = $node->getPath(); $sections = explode('/', $path, 4); return '/' . $sections[3]; } } private/Share20/DefaultShareProvider.php 0000604 00000110736 15247130452 0014224 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share20; use OC\Files\Cache\Cache; use OCP\Files\File; use OCP\Files\Folder; use OCP\Share\IShareProvider; use OC\Share20\Exception\InvalidShare; use OC\Share20\Exception\ProviderException; use OCP\Share\Exceptions\ShareNotFound; use OC\Share20\Exception\BackendError; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IGroup; use OCP\IGroupManager; use OCP\IUserManager; use OCP\Files\IRootFolder; use OCP\IDBConnection; use OCP\Files\Node; /** * Class DefaultShareProvider * * @package OC\Share20 */ class DefaultShareProvider implements IShareProvider { // Special share type for user modified group shares const SHARE_TYPE_USERGROUP = 2; /** @var IDBConnection */ private $dbConn; /** @var IUserManager */ private $userManager; /** @var IGroupManager */ private $groupManager; /** @var IRootFolder */ private $rootFolder; /** * DefaultShareProvider constructor. * * @param IDBConnection $connection * @param IUserManager $userManager * @param IGroupManager $groupManager * @param IRootFolder $rootFolder */ public function __construct( IDBConnection $connection, IUserManager $userManager, IGroupManager $groupManager, IRootFolder $rootFolder) { $this->dbConn = $connection; $this->userManager = $userManager; $this->groupManager = $groupManager; $this->rootFolder = $rootFolder; } /** * Return the identifier of this provider. * * @return string Containing only [a-zA-Z0-9] */ public function identifier() { return 'ocinternal'; } /** * Share a path * * @param \OCP\Share\IShare $share * @return \OCP\Share\IShare The share object * @throws ShareNotFound * @throws \Exception */ public function create(\OCP\Share\IShare $share) { $qb = $this->dbConn->getQueryBuilder(); $qb->insert('share'); $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType())); if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { //Set the UID of the user we share with $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { //Set the GID of the group we share with $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { //Set the token of the share $qb->setValue('token', $qb->createNamedParameter($share->getToken())); //If a password is set store it if ($share->getPassword() !== null) { $qb->setValue('password', $qb->createNamedParameter($share->getPassword())); } //If an expiration date is set store it if ($share->getExpirationDate() !== null) { $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); } if (method_exists($share, 'getParent')) { $qb->setValue('parent', $qb->createNamedParameter($share->getParent())); } } else { throw new \Exception('invalid share type!'); } // Set what is shares $qb->setValue('item_type', $qb->createParameter('itemType')); if ($share->getNode() instanceof \OCP\Files\File) { $qb->setParameter('itemType', 'file'); } else { $qb->setParameter('itemType', 'folder'); } // Set the file id $qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId())); $qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId())); // set the permissions $qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions())); // Set who created this share $qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy())); // Set who is the owner of this file/folder (and this the owner of the share) $qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner())); // Set the file target $qb->setValue('file_target', $qb->createNamedParameter($share->getTarget())); // Set the time this share was created $qb->setValue('stime', $qb->createNamedParameter(time())); // insert the data and fetch the id of the share $this->dbConn->beginTransaction(); $qb->execute(); $id = $this->dbConn->lastInsertId('*PREFIX*share'); // Now fetch the inserted share and create a complete share object $qb = $this->dbConn->getQueryBuilder(); $qb->select('*') ->from('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); $cursor = $qb->execute(); $data = $cursor->fetch(); $this->dbConn->commit(); $cursor->closeCursor(); if ($data === false) { throw new ShareNotFound(); } $share = $this->createShare($data); return $share; } /** * Update a share * * @param \OCP\Share\IShare $share * @return \OCP\Share\IShare The share object */ public function update(\OCP\Share\IShare $share) { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { /* * We allow updating the recipient on user shares. */ $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) ->set('share_with', $qb->createNamedParameter($share->getSharedWith())) ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->execute(); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->execute(); /* * Update all user defined group shares */ $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->execute(); /* * Now update the permissions for all children that have not set it to 0 */ $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0))) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) ->execute(); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) ->set('password', $qb->createNamedParameter($share->getPassword())) ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('token', $qb->createNamedParameter($share->getToken())) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->execute(); } return $share; } /** * Get all children of this share * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in * * @param \OCP\Share\IShare $parent * @return \OCP\Share\IShare[] */ public function getChildren(\OCP\Share\IShare $parent) { $children = []; $qb = $this->dbConn->getQueryBuilder(); $qb->select('*') ->from('share') ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) ->andWhere( $qb->expr()->in( 'share_type', $qb->createNamedParameter([ \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, ], IQueryBuilder::PARAM_INT_ARRAY) ) ) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) ->orderBy('id'); $cursor = $qb->execute(); while($data = $cursor->fetch()) { $children[] = $this->createShare($data); } $cursor->closeCursor(); return $children; } /** * Delete a share * * @param \OCP\Share\IShare $share */ public function delete(\OCP\Share\IShare $share) { $qb = $this->dbConn->getQueryBuilder(); $qb->delete('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))); /* * If the share is a group share delete all possible * user defined groups shares. */ if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))); } $qb->execute(); } /** * Unshare a share from the recipient. If this is a group share * this means we need a special entry in the share db. * * @param \OCP\Share\IShare $share * @param string $recipient UserId of recipient * @throws BackendError * @throws ProviderException */ public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $group = $this->groupManager->get($share->getSharedWith()); $user = $this->userManager->get($recipient); if (is_null($group)) { throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist'); } if (!$group->inGroup($user)) { throw new ProviderException('Recipient not in receiving group'); } // Try to fetch user specific share $qb = $this->dbConn->getQueryBuilder(); $stmt = $qb->select('*') ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) ->execute(); $data = $stmt->fetch(); /* * Check if there already is a user specific group share. * If there is update it (if required). */ if ($data === false) { $qb = $this->dbConn->getQueryBuilder(); $type = $share->getNodeType(); //Insert new share $qb->insert('share') ->values([ 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), 'share_with' => $qb->createNamedParameter($recipient), 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), 'parent' => $qb->createNamedParameter($share->getId()), 'item_type' => $qb->createNamedParameter($type), 'item_source' => $qb->createNamedParameter($share->getNodeId()), 'file_source' => $qb->createNamedParameter($share->getNodeId()), 'file_target' => $qb->createNamedParameter($share->getTarget()), 'permissions' => $qb->createNamedParameter(0), 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), ])->execute(); } else if ($data['permissions'] !== 0) { // Update existing usergroup share $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->set('permissions', $qb->createNamedParameter(0)) ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) ->execute(); } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { if ($share->getSharedWith() !== $recipient) { throw new ProviderException('Recipient does not match'); } // We can just delete user and link shares $this->delete($share); } else { throw new ProviderException('Invalid shareType'); } } /** * @inheritdoc */ public function move(\OCP\Share\IShare $share, $recipient) { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { // Just update the target $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->set('file_target', $qb->createNamedParameter($share->getTarget())) ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) ->execute(); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { // Check if there is a usergroup share $qb = $this->dbConn->getQueryBuilder(); $stmt = $qb->select('id') ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) ->setMaxResults(1) ->execute(); $data = $stmt->fetch(); $stmt->closeCursor(); if ($data === false) { // No usergroup share yet. Create one. $qb = $this->dbConn->getQueryBuilder(); $qb->insert('share') ->values([ 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), 'share_with' => $qb->createNamedParameter($recipient), 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), 'parent' => $qb->createNamedParameter($share->getId()), 'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'), 'item_source' => $qb->createNamedParameter($share->getNode()->getId()), 'file_source' => $qb->createNamedParameter($share->getNode()->getId()), 'file_target' => $qb->createNamedParameter($share->getTarget()), 'permissions' => $qb->createNamedParameter($share->getPermissions()), 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), ])->execute(); } else { // Already a usergroup share. Update it. $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->set('file_target', $qb->createNamedParameter($share->getTarget())) ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) ->execute(); } } return $share; } public function getSharesInFolder($userId, Folder $node, $reshares) { $qb = $this->dbConn->getQueryBuilder(); $qb->select('*') ->from('share', 's') ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )); $qb->andWhere($qb->expr()->orX( $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)), $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)), $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)) )); /** * Reshares for this user are shares where they are the owner. */ if ($reshares === false) { $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); } else { $qb->andWhere( $qb->expr()->orX( $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) ) ); } $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); $qb->orderBy('id'); $cursor = $qb->execute(); $shares = []; while ($data = $cursor->fetch()) { $shares[$data['fileid']][] = $this->createShare($data); } $cursor->closeCursor(); return $shares; } /** * @inheritdoc */ public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { $qb = $this->dbConn->getQueryBuilder(); $qb->select('*') ->from('share') ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )); $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType))); /** * Reshares for this user are shares where they are the owner. */ if ($reshares === false) { $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); } else { $qb->andWhere( $qb->expr()->orX( $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) ) ); } if ($node !== null) { $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); } if ($limit !== -1) { $qb->setMaxResults($limit); } $qb->setFirstResult($offset); $qb->orderBy('id'); $cursor = $qb->execute(); $shares = []; while($data = $cursor->fetch()) { $shares[] = $this->createShare($data); } $cursor->closeCursor(); return $shares; } /** * @inheritdoc */ public function getShareById($id, $recipientId = null) { $qb = $this->dbConn->getQueryBuilder(); $qb->select('*') ->from('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->andWhere( $qb->expr()->in( 'share_type', $qb->createNamedParameter([ \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, ], IQueryBuilder::PARAM_INT_ARRAY) ) ) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )); $cursor = $qb->execute(); $data = $cursor->fetch(); $cursor->closeCursor(); if ($data === false) { throw new ShareNotFound(); } try { $share = $this->createShare($data); } catch (InvalidShare $e) { throw new ShareNotFound(); } // If the recipient is set for a group share resolve to that user if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $share = $this->resolveGroupShares([$share], $recipientId)[0]; } return $share; } /** * Get shares for a given path * * @param \OCP\Files\Node $path * @return \OCP\Share\IShare[] */ public function getSharesByPath(Node $path) { $qb = $this->dbConn->getQueryBuilder(); $cursor = $qb->select('*') ->from('share') ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) ->andWhere( $qb->expr()->orX( $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)), $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)) ) ) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) ->execute(); $shares = []; while($data = $cursor->fetch()) { $shares[] = $this->createShare($data); } $cursor->closeCursor(); return $shares; } /** * Returns whether the given database result can be interpreted as * a share with accessible file (not trashed, not deleted) */ private function isAccessibleResult($data) { // exclude shares leading to deleted file entries if ($data['fileid'] === null) { return false; } // exclude shares leading to trashbin on home storages $pathSections = explode('/', $data['path'], 2); // FIXME: would not detect rare md5'd home storage case properly if ($pathSections[0] !== 'files' && in_array(explode(':', $data['storage_string_id'], 2)[0], array('home', 'object'))) { return false; } return true; } /** * @inheritdoc */ public function getSharedWith($userId, $shareType, $node, $limit, $offset) { /** @var Share[] $shares */ $shares = []; if ($shareType === \OCP\Share::SHARE_TYPE_USER) { //Get shares directly with this user $qb = $this->dbConn->getQueryBuilder(); $qb->select('s.*', 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash', 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime', 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum' ) ->selectAlias('st.id', 'storage_string_id') ->from('share', 's') ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')); // Order by id $qb->orderBy('s.id'); // Set limit and offset if ($limit !== -1) { $qb->setMaxResults($limit); } $qb->setFirstResult($offset); $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )); // Filter by node if provided if ($node !== null) { $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); } $cursor = $qb->execute(); while($data = $cursor->fetch()) { if ($this->isAccessibleResult($data)) { $shares[] = $this->createShare($data); } } $cursor->closeCursor(); } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { $user = $this->userManager->get($userId); $allGroups = $this->groupManager->getUserGroups($user); /** @var Share[] $shares2 */ $shares2 = []; $start = 0; while(true) { $groups = array_slice($allGroups, $start, 100); $start += 100; if ($groups === []) { break; } $qb = $this->dbConn->getQueryBuilder(); $qb->select('s.*', 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash', 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime', 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum' ) ->selectAlias('st.id', 'storage_string_id') ->from('share', 's') ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')) ->orderBy('s.id') ->setFirstResult(0); if ($limit !== -1) { $qb->setMaxResults($limit - count($shares)); } // Filter by node if provided if ($node !== null) { $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); } $groups = array_filter($groups, function($group) { return $group instanceof IGroup; }); $groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups); $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter( $groups, IQueryBuilder::PARAM_STR_ARRAY ))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )); $cursor = $qb->execute(); while($data = $cursor->fetch()) { if ($offset > 0) { $offset--; continue; } if ($this->isAccessibleResult($data)) { $shares2[] = $this->createShare($data); } } $cursor->closeCursor(); } /* * Resolve all group shares to user specific shares */ $shares = $this->resolveGroupShares($shares2, $userId); } else { throw new BackendError('Invalid backend'); } return $shares; } /** * Get a share by token * * @param string $token * @return \OCP\Share\IShare * @throws ShareNotFound */ public function getShareByToken($token) { $qb = $this->dbConn->getQueryBuilder(); $cursor = $qb->select('*') ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))) ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) ->execute(); $data = $cursor->fetch(); if ($data === false) { throw new ShareNotFound(); } try { $share = $this->createShare($data); } catch (InvalidShare $e) { throw new ShareNotFound(); } return $share; } /** * Create a share object from an database row * * @param mixed[] $data * @return \OCP\Share\IShare * @throws InvalidShare */ private function createShare($data) { $share = new Share($this->rootFolder, $this->userManager); $share->setId((int)$data['id']) ->setShareType((int)$data['share_type']) ->setPermissions((int)$data['permissions']) ->setTarget($data['file_target']) ->setMailSend((bool)$data['mail_send']); $shareTime = new \DateTime(); $shareTime->setTimestamp((int)$data['stime']); $share->setShareTime($shareTime); if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { $share->setSharedWith($data['share_with']); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $share->setSharedWith($data['share_with']); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { $share->setPassword($data['password']); $share->setToken($data['token']); } $share->setSharedBy($data['uid_initiator']); $share->setShareOwner($data['uid_owner']); $share->setNodeId((int)$data['file_source']); $share->setNodeType($data['item_type']); if ($data['expiration'] !== null) { $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); $share->setExpirationDate($expiration); } if (isset($data['f_permissions'])) { $entryData = $data; $entryData['permissions'] = $entryData['f_permissions']; $entryData['parent'] = $entryData['f_parent'];; $share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData, \OC::$server->getMimeTypeLoader())); } $share->setProviderId($this->identifier()); return $share; } /** * @param Share[] $shares * @param $userId * @return Share[] The updates shares if no update is found for a share return the original */ private function resolveGroupShares($shares, $userId) { $result = []; $start = 0; while(true) { /** @var Share[] $shareSlice */ $shareSlice = array_slice($shares, $start, 100); $start += 100; if ($shareSlice === []) { break; } /** @var int[] $ids */ $ids = []; /** @var Share[] $shareMap */ $shareMap = []; foreach ($shareSlice as $share) { $ids[] = (int)$share->getId(); $shareMap[$share->getId()] = $share; } $qb = $this->dbConn->getQueryBuilder(); $query = $qb->select('*') ->from('share') ->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )); $stmt = $query->execute(); while($data = $stmt->fetch()) { $shareMap[$data['parent']]->setPermissions((int)$data['permissions']); $shareMap[$data['parent']]->setTarget($data['file_target']); } $stmt->closeCursor(); foreach ($shareMap as $share) { $result[] = $share; } } return $result; } /** * A user is deleted from the system * So clean up the relevant shares. * * @param string $uid * @param int $shareType */ public function userDeleted($uid, $shareType) { $qb = $this->dbConn->getQueryBuilder(); $qb->delete('share'); if ($shareType === \OCP\Share::SHARE_TYPE_USER) { /* * Delete all user shares that are owned by this user * or that are received by this user */ $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER))); $qb->andWhere( $qb->expr()->orX( $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)), $qb->expr()->eq('share_with', $qb->createNamedParameter($uid)) ) ); } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { /* * Delete all group shares that are owned by this user * Or special user group shares that are received by this user */ $qb->where( $qb->expr()->andX( $qb->expr()->orX( $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)), $qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)) ), $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)) ) ); $qb->orWhere( $qb->expr()->andX( $qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)), $qb->expr()->eq('share_with', $qb->createNamedParameter($uid)) ) ); } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { /* * Delete all link shares owned by this user. * And all link shares initiated by this user (until #22327 is in) */ $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))); $qb->andWhere( $qb->expr()->orX( $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)), $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid)) ) ); } $qb->execute(); } /** * Delete all shares received by this group. As well as any custom group * shares for group members. * * @param string $gid */ public function groupDeleted($gid) { /* * First delete all custom group shares for group members */ $qb = $this->dbConn->getQueryBuilder(); $qb->select('id') ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); $cursor = $qb->execute(); $ids = []; while($row = $cursor->fetch()) { $ids[] = (int)$row['id']; } $cursor->closeCursor(); if (!empty($ids)) { $chunks = array_chunk($ids, 100); foreach ($chunks as $chunk) { $qb->delete('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY))); $qb->execute(); } } /* * Now delete all the group shares */ $qb = $this->dbConn->getQueryBuilder(); $qb->delete('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); $qb->execute(); } /** * Delete custom group shares to this group for this user * * @param string $uid * @param string $gid */ public function userDeletedFromGroup($uid, $gid) { /* * Get all group shares */ $qb = $this->dbConn->getQueryBuilder(); $qb->select('id') ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); $cursor = $qb->execute(); $ids = []; while($row = $cursor->fetch()) { $ids[] = (int)$row['id']; } $cursor->closeCursor(); if (!empty($ids)) { $chunks = array_chunk($ids, 100); foreach ($chunks as $chunk) { /* * Delete all special shares wit this users for the found group shares */ $qb->delete('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid))) ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY))); $qb->execute(); } } } /** * @inheritdoc */ public function getAccessList($nodes, $currentAccess) { $ids = []; foreach ($nodes as $node) { $ids[] = $node->getId(); } $qb = $this->dbConn->getQueryBuilder(); $or = $qb->expr()->orX( $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)), $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)), $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)) ); if ($currentAccess) { $or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))); } $qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions') ->from('share') ->where( $or ) ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )); $cursor = $qb->execute(); $users = []; $link = false; while($row = $cursor->fetch()) { $type = (int)$row['share_type']; if ($type === \OCP\Share::SHARE_TYPE_USER) { $uid = $row['share_with']; $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; $users[$uid][$row['id']] = $row; } else if ($type === \OCP\Share::SHARE_TYPE_GROUP) { $gid = $row['share_with']; $group = $this->groupManager->get($gid); if ($group === null) { continue; } $userList = $group->getUsers(); foreach ($userList as $user) { $uid = $user->getUID(); $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; $users[$uid][$row['id']] = $row; } } else if ($type === \OCP\Share::SHARE_TYPE_LINK) { $link = true; } else if ($type === self::SHARE_TYPE_USERGROUP && $currentAccess === true) { $uid = $row['share_with']; $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; $users[$uid][$row['id']] = $row; } } $cursor->closeCursor(); if ($currentAccess === true) { $users = array_map([$this, 'filterSharesOfUser'], $users); $users = array_filter($users); } else { $users = array_keys($users); } return ['users' => $users, 'public' => $link]; } /** * For each user the path with the fewest slashes is returned * @param array $shares * @return array */ protected function filterSharesOfUser(array $shares) { // Group shares when the user has a share exception foreach ($shares as $id => $share) { $type = (int) $share['share_type']; $permissions = (int) $share['permissions']; if ($type === self::SHARE_TYPE_USERGROUP) { unset($shares[$share['parent']]); if ($permissions === 0) { unset($shares[$id]); } } } $best = []; $bestDepth = 0; foreach ($shares as $id => $share) { $depth = substr_count($share['file_target'], '/'); if (empty($best) || $depth < $bestDepth) { $bestDepth = $depth; $best = [ 'node_id' => $share['file_source'], 'node_path' => $share['file_target'], ]; } } return $best; } } private/Share20/Share.php 0000604 00000017606 15247130452 0011206 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share20; use OCP\Files\Cache\ICacheEntry; use OCP\Files\File; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\IUserManager; use OCP\Share\Exceptions\IllegalIDChangeException; class Share implements \OCP\Share\IShare { /** @var string */ private $id; /** @var string */ private $providerId; /** @var Node */ private $node; /** @var int */ private $fileId; /** @var string */ private $nodeType; /** @var int */ private $shareType; /** @var string */ private $sharedWith; /** @var string */ private $sharedBy; /** @var string */ private $shareOwner; /** @var int */ private $permissions; /** @var \DateTime */ private $expireDate; /** @var string */ private $password; /** @var string */ private $token; /** @var int */ private $parent; /** @var string */ private $target; /** @var \DateTime */ private $shareTime; /** @var bool */ private $mailSend; /** @var IRootFolder */ private $rootFolder; /** @var IUserManager */ private $userManager; /** @var ICacheEntry|null */ private $nodeCacheEntry; public function __construct(IRootFolder $rootFolder, IUserManager $userManager) { $this->rootFolder = $rootFolder; $this->userManager = $userManager; } /** * @inheritdoc */ public function setId($id) { if (is_int($id)) { $id = (string)$id; } if(!is_string($id)) { throw new \InvalidArgumentException('String expected.'); } if ($this->id !== null) { throw new IllegalIDChangeException('Not allowed to assign a new internal id to a share'); } $this->id = trim($id); return $this; } /** * @inheritdoc */ public function getId() { return $this->id; } /** * @inheritdoc */ public function getFullId() { if ($this->providerId === null || $this->id === null) { throw new \UnexpectedValueException; } return $this->providerId . ':' . $this->id; } /** * @inheritdoc */ public function setProviderId($id) { if(!is_string($id)) { throw new \InvalidArgumentException('String expected.'); } if ($this->providerId !== null) { throw new IllegalIDChangeException('Not allowed to assign a new provider id to a share'); } $this->providerId = trim($id); return $this; } /** * @inheritdoc */ public function setNode(Node $node) { $this->fileId = null; $this->nodeType = null; $this->node = $node; return $this; } /** * @inheritdoc */ public function getNode() { if ($this->node === null) { if ($this->shareOwner === null || $this->fileId === null) { throw new NotFoundException(); } // for federated shares the owner can be a remote user, in this // case we use the initiator if($this->userManager->userExists($this->shareOwner)) { $userFolder = $this->rootFolder->getUserFolder($this->shareOwner); } else { $userFolder = $this->rootFolder->getUserFolder($this->sharedBy); } $nodes = $userFolder->getById($this->fileId); if (empty($nodes)) { throw new NotFoundException('Node for share not found, fileid: ' . $this->fileId); } $this->node = $nodes[0]; } return $this->node; } /** * @inheritdoc */ public function setNodeId($fileId) { $this->node = null; $this->fileId = $fileId; return $this; } /** * @inheritdoc */ public function getNodeId() { if ($this->fileId === null) { $this->fileId = $this->getNode()->getId(); } return $this->fileId; } /** * @inheritdoc */ public function setNodeType($type) { if ($type !== 'file' && $type !== 'folder') { throw new \InvalidArgumentException(); } $this->nodeType = $type; return $this; } /** * @inheritdoc */ public function getNodeType() { if ($this->nodeType === null) { $node = $this->getNode(); $this->nodeType = $node instanceof File ? 'file' : 'folder'; } return $this->nodeType; } /** * @inheritdoc */ public function setShareType($shareType) { $this->shareType = $shareType; return $this; } /** * @inheritdoc */ public function getShareType() { return $this->shareType; } /** * @inheritdoc */ public function setSharedWith($sharedWith) { if (!is_string($sharedWith)) { throw new \InvalidArgumentException(); } $this->sharedWith = $sharedWith; return $this; } /** * @inheritdoc */ public function getSharedWith() { return $this->sharedWith; } /** * @inheritdoc */ public function setPermissions($permissions) { //TODO checkes $this->permissions = $permissions; return $this; } /** * @inheritdoc */ public function getPermissions() { return $this->permissions; } /** * @inheritdoc */ public function setExpirationDate($expireDate) { //TODO checks $this->expireDate = $expireDate; return $this; } /** * @inheritdoc */ public function getExpirationDate() { return $this->expireDate; } /** * @inheritdoc */ public function setSharedBy($sharedBy) { if (!is_string($sharedBy)) { throw new \InvalidArgumentException(); } //TODO checks $this->sharedBy = $sharedBy; return $this; } /** * @inheritdoc */ public function getSharedBy() { //TODO check if set return $this->sharedBy; } /** * @inheritdoc */ public function setShareOwner($shareOwner) { if (!is_string($shareOwner)) { throw new \InvalidArgumentException(); } //TODO checks $this->shareOwner = $shareOwner; return $this; } /** * @inheritdoc */ public function getShareOwner() { //TODO check if set return $this->shareOwner; } /** * @inheritdoc */ public function setPassword($password) { $this->password = $password; return $this; } /** * @inheritdoc */ public function getPassword() { return $this->password; } /** * @inheritdoc */ public function setToken($token) { $this->token = $token; return $this; } /** * @inheritdoc */ public function getToken() { return $this->token; } /** * Set the parent of this share * * @param int parent * @return \OCP\Share\IShare * @deprecated The new shares do not have parents. This is just here for legacy reasons. */ public function setParent($parent) { $this->parent = $parent; return $this; } /** * Get the parent of this share. * * @return int * @deprecated The new shares do not have parents. This is just here for legacy reasons. */ public function getParent() { return $this->parent; } /** * @inheritdoc */ public function setTarget($target) { $this->target = $target; return $this; } /** * @inheritdoc */ public function getTarget() { return $this->target; } /** * @inheritdoc */ public function setShareTime(\DateTime $shareTime) { $this->shareTime = $shareTime; return $this; } /** * @inheritdoc */ public function getShareTime() { return $this->shareTime; } /** * @inheritdoc */ public function setMailSend($mailSend) { $this->mailSend = $mailSend; return $this; } /** * @inheritdoc */ public function getMailSend() { return $this->mailSend; } /** * @inheritdoc */ public function setNodeCacheEntry(ICacheEntry $entry) { $this->nodeCacheEntry = $entry; } /** * @inheritdoc */ public function getNodeCacheEntry() { return $this->nodeCacheEntry; } } private/Share20/LegacyHooks.php 0000604 00000005663 15247130452 0012354 0 ustar 00 <?php /** * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Share20; use OCP\Share\IShare; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\GenericEvent; class LegacyHooks { /** @var EventDispatcher */ private $eventDispatcher; /** * LegacyHooks constructor. * * @param EventDispatcher $eventDispatcher */ public function __construct(EventDispatcher $eventDispatcher) { $this->eventDispatcher = $eventDispatcher; $this->eventDispatcher->addListener('OCP\Share::preUnshare', [$this, 'preUnshare']); $this->eventDispatcher->addListener('OCP\Share::postUnshare', [$this, 'postUnshare']); } /** * @param GenericEvent $e */ public function preUnshare(GenericEvent $e) { /** @var IShare $share */ $share = $e->getSubject(); $formatted = $this->formatHookParams($share); \OC_Hook::emit('OCP\Share', 'pre_unshare', $formatted); } /** * @param GenericEvent $e */ public function postUnshare(GenericEvent $e) { /** @var IShare $share */ $share = $e->getSubject(); $formatted = $this->formatHookParams($share); /** @var IShare[] $deletedShares */ $deletedShares = $e->getArgument('deletedShares'); $formattedDeletedShares = array_map(function($share) { return $this->formatHookParams($share); }, $deletedShares); $formatted['deletedShares'] = $formattedDeletedShares; \OC_Hook::emit('OCP\Share', 'post_unshare', $formatted); } private function formatHookParams(IShare $share) { // Prepare hook $shareType = $share->getShareType(); $sharedWith = ''; if ($shareType === \OCP\Share::SHARE_TYPE_USER || $shareType === \OCP\Share::SHARE_TYPE_GROUP || $shareType === \OCP\Share::SHARE_TYPE_REMOTE) { $sharedWith = $share->getSharedWith(); } $hookParams = [ 'id' => $share->getId(), 'itemType' => $share->getNodeType(), 'itemSource' => $share->getNodeId(), 'shareType' => $shareType, 'shareWith' => $sharedWith, 'itemparent' => method_exists($share, 'getParent') ? $share->getParent() : '', 'uidOwner' => $share->getSharedBy(), 'fileSource' => $share->getNodeId(), 'fileTarget' => $share->getTarget() ]; return $hookParams; } } private/Share20/Exception/BackendError.php 0000604 00000001503 15247130452 0014430 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share20\Exception; class BackendError extends \Exception { } private/Share20/Exception/ProviderException.php 0000604 00000001512 15247130452 0015540 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share20\Exception; class ProviderException extends \Exception { } private/Share20/Exception/InvalidShare.php 0000604 00000001503 15247130452 0014440 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share20\Exception; class InvalidShare extends \Exception { } private/Log.php 0000604 00000022301 15247130452 0007445 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use InterfaSys\LogNormalizer\Normalizer; use \OCP\ILogger; use OCP\Util; /** * logging utilities * * This is a stand in, this should be replaced by a Psr\Log\LoggerInterface * compatible logger. See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md * for the full interface specification. * * MonoLog is an example implementing this interface. */ class Log implements ILogger { /** @var string */ private $logger; /** @var SystemConfig */ private $config; /** @var boolean|null cache the result of the log condition check for the request */ private $logConditionSatisfied = null; /** @var Normalizer */ private $normalizer; protected $methodsWithSensitiveParameters = [ // Session/User 'completeLogin', 'login', 'checkPassword', 'checkPasswordNoLogging', 'loginWithPassword', 'updatePrivateKeyPassword', 'validateUserPass', // TokenProvider 'getToken', 'isTokenPassword', 'getPassword', 'decryptPassword', 'logClientIn', 'generateToken', 'validateToken', // TwoFactorAuth 'solveChallenge', 'verifyChallenge', // ICrypto 'calculateHMAC', 'encrypt', 'decrypt', // LoginController 'tryLogin', 'confirmPassword', // LDAP 'bind', 'areCredentialsValid', 'invokeLDAPMethod', ]; /** * @param string $logger The logger that should be used * @param SystemConfig $config the system config object * @param null $normalizer */ public function __construct($logger = null, SystemConfig $config = null, $normalizer = null) { // FIXME: Add this for backwards compatibility, should be fixed at some point probably if($config === null) { $config = \OC::$server->getSystemConfig(); } $this->config = $config; // FIXME: Add this for backwards compatibility, should be fixed at some point probably if($logger === null) { $logType = $this->config->getValue('log_type', 'file'); $this->logger = static::getLogClass($logType); call_user_func(array($this->logger, 'init')); } else { $this->logger = $logger; } if ($normalizer === null) { $this->normalizer = new Normalizer(); } else { $this->normalizer = $normalizer; } } /** * System is unusable. * * @param string $message * @param array $context * @return void */ public function emergency($message, array $context = array()) { $this->log(Util::FATAL, $message, $context); } /** * Action must be taken immediately. * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * * @param string $message * @param array $context * @return void */ public function alert($message, array $context = array()) { $this->log(Util::ERROR, $message, $context); } /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * * @param string $message * @param array $context * @return void */ public function critical($message, array $context = array()) { $this->log(Util::ERROR, $message, $context); } /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message * @param array $context * @return void */ public function error($message, array $context = array()) { $this->log(Util::ERROR, $message, $context); } /** * Exceptional occurrences that are not errors. * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * * @param string $message * @param array $context * @return void */ public function warning($message, array $context = array()) { $this->log(Util::WARN, $message, $context); } /** * Normal but significant events. * * @param string $message * @param array $context * @return void */ public function notice($message, array $context = array()) { $this->log(Util::INFO, $message, $context); } /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string $message * @param array $context * @return void */ public function info($message, array $context = array()) { $this->log(Util::INFO, $message, $context); } /** * Detailed debug information. * * @param string $message * @param array $context * @return void */ public function debug($message, array $context = array()) { $this->log(Util::DEBUG, $message, $context); } /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context * @return void */ public function log($level, $message, array $context = array()) { $minLevel = min($this->config->getValue('loglevel', Util::WARN), Util::FATAL); $logCondition = $this->config->getValue('log.condition', []); array_walk($context, [$this->normalizer, 'format']); if (isset($context['app'])) { $app = $context['app']; /** * check log condition based on the context of each log message * once this is met -> change the required log level to debug */ if(!empty($logCondition) && isset($logCondition['apps']) && in_array($app, $logCondition['apps'], true)) { $minLevel = Util::DEBUG; } } else { $app = 'no app in context'; } // interpolate $message as defined in PSR-3 $replace = array(); foreach ($context as $key => $val) { $replace['{' . $key . '}'] = $val; } // interpolate replacement values into the message and return $message = strtr($message, $replace); /** * check for a special log condition - this enables an increased log on * a per request/user base */ if($this->logConditionSatisfied === null) { // default to false to just process this once per request $this->logConditionSatisfied = false; if(!empty($logCondition)) { // check for secret token in the request if(isset($logCondition['shared_secret'])) { $request = \OC::$server->getRequest(); // if token is found in the request change set the log condition to satisfied if($request && hash_equals($logCondition['shared_secret'], $request->getParam('log_secret', ''))) { $this->logConditionSatisfied = true; } } // check for user if(isset($logCondition['users'])) { $user = \OC::$server->getUserSession()->getUser(); // if the user matches set the log condition to satisfied if($user !== null && in_array($user->getUID(), $logCondition['users'], true)) { $this->logConditionSatisfied = true; } } } } // if log condition is satisfied change the required log level to DEBUG if($this->logConditionSatisfied) { $minLevel = Util::DEBUG; } if ($level >= $minLevel) { $logger = $this->logger; call_user_func(array($logger, 'write'), $app, $message, $level); } } /** * Logs an exception very detailed * * @param \Exception|\Throwable $exception * @param array $context * @return void * @since 8.2.0 */ public function logException($exception, array $context = array()) { $level = Util::ERROR; if (isset($context['level'])) { $level = $context['level']; unset($context['level']); } $data = array( 'Exception' => get_class($exception), 'Message' => $exception->getMessage(), 'Code' => $exception->getCode(), 'Trace' => $exception->getTraceAsString(), 'File' => $exception->getFile(), 'Line' => $exception->getLine(), ); $data['Trace'] = preg_replace('!(' . implode('|', $this->methodsWithSensitiveParameters) . ')\(.*\)!', '$1(*** sensitive parameters replaced ***)', $data['Trace']); $msg = isset($context['message']) ? $context['message'] : 'Exception'; $msg .= ': ' . json_encode($data); $this->log($level, $msg, $context); } /** * @param string $logType * @return string * @internal */ public static function getLogClass($logType) { switch (strtolower($logType)) { case 'errorlog': return \OC\Log\Errorlog::class; case 'syslog': return \OC\Log\Syslog::class; case 'file': return \OC\Log\File::class; // Backwards compatibility for old and fallback for unknown log types case 'owncloud': case 'nextcloud': default: return \OC\Log\File::class; } } } private/TagManager.php 0000604 00000005270 15247130452 0010740 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Reiter <ockham@raz.or.at> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Factory class creating instances of \OCP\ITags * * A tag can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or * anything else that is either parsed from a vobject or that the user chooses * to add. * Tag names are not case-sensitive, but will be saved with the case they * are entered in. If a user already has a tag 'family' for a type, and * tries to add a tag named 'Family' it will be silently ignored. */ namespace OC; use OC\Tagging\TagMapper; class TagManager implements \OCP\ITagManager { /** * User session * * @var \OCP\IUserSession */ private $userSession; /** * TagMapper * * @var TagMapper */ private $mapper; /** * Constructor. * * @param TagMapper $mapper Instance of the TagMapper abstraction layer. * @param \OCP\IUserSession $userSession the user session */ public function __construct(TagMapper $mapper, \OCP\IUserSession $userSession) { $this->mapper = $mapper; $this->userSession = $userSession; } /** * Create a new \OCP\ITags instance and load tags from db. * * @see \OCP\ITags * @param string $type The type identifier e.g. 'contact' or 'event'. * @param array $defaultTags An array of default tags to be used if none are stored. * @param boolean $includeShared Whether to include tags for items shared with this user by others. * @param string $userId user for which to retrieve the tags, defaults to the currently * logged in user * @return \OCP\ITags */ public function load($type, $defaultTags = array(), $includeShared = false, $userId = null) { if (is_null($userId)) { $user = $this->userSession->getUser(); if ($user === null) { // nothing we can do without a user return null; } $userId = $this->userSession->getUser()->getUId(); } return new Tags($this->mapper, $userId, $type, $defaultTags, $includeShared); } } private/DateTimeFormatter.php 0000604 00000024326 15247130452 0012315 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class DateTimeFormatter implements \OCP\IDateTimeFormatter { /** @var \DateTimeZone */ protected $defaultTimeZone; /** @var \OCP\IL10N */ protected $defaultL10N; /** * Constructor * * @param \DateTimeZone $defaultTimeZone Set the timezone for the format * @param \OCP\IL10N $defaultL10N Set the language for the format */ public function __construct(\DateTimeZone $defaultTimeZone, \OCP\IL10N $defaultL10N) { $this->defaultTimeZone = $defaultTimeZone; $this->defaultL10N = $defaultL10N; } /** * Get TimeZone to use * * @param \DateTimeZone $timeZone The timezone to use * @return \DateTimeZone The timezone to use, falling back to the current user's timezone */ protected function getTimeZone($timeZone = null) { if ($timeZone === null) { $timeZone = $this->defaultTimeZone; } return $timeZone; } /** * Get \OCP\IL10N to use * * @param \OCP\IL10N $l The locale to use * @return \OCP\IL10N The locale to use, falling back to the current user's locale */ protected function getLocale($l = null) { if ($l === null) { $l = $this->defaultL10N; } return $l; } /** * Generates a DateTime object with the given timestamp and TimeZone * * @param mixed $timestamp * @param \DateTimeZone $timeZone The timezone to use * @return \DateTime */ protected function getDateTime($timestamp, \DateTimeZone $timeZone = null) { if ($timestamp === null) { return new \DateTime('now', $timeZone); } else if (!$timestamp instanceof \DateTime) { $dateTime = new \DateTime('now', $timeZone); $dateTime->setTimestamp($timestamp); return $dateTime; } if ($timeZone) { $timestamp->setTimezone($timeZone); } return $timestamp; } /** * Formats the date of the given timestamp * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param string $format Either 'full', 'long', 'medium' or 'short' * full: e.g. 'EEEE, MMMM d, y' => 'Wednesday, August 20, 2014' * long: e.g. 'MMMM d, y' => 'August 20, 2014' * medium: e.g. 'MMM d, y' => 'Aug 20, 2014' * short: e.g. 'M/d/yy' => '8/20/14' * The exact format is dependent on the language * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted date string */ public function formatDate($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { return $this->format($timestamp, 'date', $format, $timeZone, $l); } /** * Formats the date of the given timestamp * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param string $format Either 'full', 'long', 'medium' or 'short' * full: e.g. 'EEEE, MMMM d, y' => 'Wednesday, August 20, 2014' * long: e.g. 'MMMM d, y' => 'August 20, 2014' * medium: e.g. 'MMM d, y' => 'Aug 20, 2014' * short: e.g. 'M/d/yy' => '8/20/14' * The exact format is dependent on the language * Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted relative date string */ public function formatDateRelativeDay($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { if (substr($format, -1) !== '*' && substr($format, -1) !== '*') { $format .= '^'; } return $this->format($timestamp, 'date', $format, $timeZone, $l); } /** * Gives the relative date of the timestamp * Only works for past dates * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param int|\DateTime $baseTimestamp Timestamp to compare $timestamp against, defaults to current time * @return string Dates returned are: * < 1 month => Today, Yesterday, n days ago * < 13 month => last month, n months ago * >= 13 month => last year, n years ago * @param \OCP\IL10N $l The locale to use * @return string Formatted date span */ public function formatDateSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null) { $l = $this->getLocale($l); $timestamp = $this->getDateTime($timestamp); $timestamp->setTime(0, 0, 0); if ($baseTimestamp === null) { $baseTimestamp = time(); } $baseTimestamp = $this->getDateTime($baseTimestamp); $baseTimestamp->setTime(0, 0, 0); $dateInterval = $timestamp->diff($baseTimestamp); if ($dateInterval->y == 0 && $dateInterval->m == 0 && $dateInterval->d == 0) { return (string) $l->t('today'); } else if ($dateInterval->y == 0 && $dateInterval->m == 0 && $dateInterval->d == 1) { return (string) $l->t('yesterday'); } else if ($dateInterval->y == 0 && $dateInterval->m == 0) { return (string) $l->n('%n day ago', '%n days ago', $dateInterval->d); } else if ($dateInterval->y == 0 && $dateInterval->m == 1) { return (string) $l->t('last month'); } else if ($dateInterval->y == 0) { return (string) $l->n('%n month ago', '%n months ago', $dateInterval->m); } else if ($dateInterval->y == 1) { return (string) $l->t('last year'); } return (string) $l->n('%n year ago', '%n years ago', $dateInterval->y); } /** * Formats the time of the given timestamp * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param string $format Either 'full', 'long', 'medium' or 'short' * full: e.g. 'h:mm:ss a zzzz' => '11:42:13 AM GMT+0:00' * long: e.g. 'h:mm:ss a z' => '11:42:13 AM GMT' * medium: e.g. 'h:mm:ss a' => '11:42:13 AM' * short: e.g. 'h:mm a' => '11:42 AM' * The exact format is dependent on the language * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted time string */ public function formatTime($timestamp, $format = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { return $this->format($timestamp, 'time', $format, $timeZone, $l); } /** * Gives the relative past time of the timestamp * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param int|\DateTime $baseTimestamp Timestamp to compare $timestamp against, defaults to current time * @return string Dates returned are: * < 60 sec => seconds ago * < 1 hour => n minutes ago * < 1 day => n hours ago * < 1 month => Yesterday, n days ago * < 13 month => last month, n months ago * >= 13 month => last year, n years ago * @param \OCP\IL10N $l The locale to use * @return string Formatted time span */ public function formatTimeSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null) { $l = $this->getLocale($l); $timestamp = $this->getDateTime($timestamp); if ($baseTimestamp === null) { $baseTimestamp = time(); } $baseTimestamp = $this->getDateTime($baseTimestamp); $diff = $timestamp->diff($baseTimestamp); if ($diff->y > 0 || $diff->m > 0 || $diff->d > 0) { return (string) $this->formatDateSpan($timestamp, $baseTimestamp, $l); } if ($diff->h > 0) { return (string) $l->n('%n hour ago', '%n hours ago', $diff->h); } else if ($diff->i > 0) { return (string) $l->n('%n minute ago', '%n minutes ago', $diff->i); } return (string) $l->t('seconds ago'); } /** * Formats the date and time of the given timestamp * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param string $formatDate See formatDate() for description * @param string $formatTime See formatTime() for description * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted date and time string */ public function formatDateTime($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l); } /** * Formats the date and time of the given timestamp * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param string $formatDate See formatDate() for description * Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable * @param string $formatTime See formatTime() for description * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted relative date and time string */ public function formatDateTimeRelativeDay($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { if (substr($formatDate, -1) !== '^' && substr($formatDate, -1) !== '*') { $formatDate .= '^'; } return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l); } /** * Formats the date and time of the given timestamp * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param string $type One of 'date', 'datetime' or 'time' * @param string $format Format string * @param \DateTimeZone $timeZone The timezone to use * @param \OCP\IL10N $l The locale to use * @return string Formatted date and time string */ protected function format($timestamp, $type, $format, \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { $l = $this->getLocale($l); $timeZone = $this->getTimeZone($timeZone); $timestamp = $this->getDateTime($timestamp, $timeZone); return (string) $l->l($type, $timestamp, array( 'width' => $format, )); } } private/Settings/Manager.php 0000604 00000023272 15247130452 0012106 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings; use OCP\AppFramework\QueryException; use OCP\Encryption\IManager as EncryptionManager; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUserManager; use OCP\Lock\ILockingProvider; use OCP\Settings\ISettings; use OCP\Settings\IManager; use OCP\Settings\ISection; class Manager implements IManager { const TABLE_ADMIN_SETTINGS = 'admin_settings'; const TABLE_ADMIN_SECTIONS = 'admin_sections'; /** @var ILogger */ private $log; /** @var IDBConnection */ private $dbc; /** @var Mapper */ private $mapper; /** @var IL10N */ private $l; /** @var IConfig */ private $config; /** @var EncryptionManager */ private $encryptionManager; /** @var IUserManager */ private $userManager; /** @var ILockingProvider */ private $lockingProvider; /** @var IRequest */ private $request; /** @var IURLGenerator */ private $url; /** * @param ILogger $log * @param IDBConnection $dbc * @param IL10N $l * @param IConfig $config * @param EncryptionManager $encryptionManager * @param IUserManager $userManager * @param ILockingProvider $lockingProvider * @param IRequest $request * @param Mapper $mapper * @param IURLGenerator $url */ public function __construct( ILogger $log, IDBConnection $dbc, IL10N $l, IConfig $config, EncryptionManager $encryptionManager, IUserManager $userManager, ILockingProvider $lockingProvider, IRequest $request, Mapper $mapper, IURLGenerator $url ) { $this->log = $log; $this->dbc = $dbc; $this->mapper = $mapper; $this->l = $l; $this->config = $config; $this->encryptionManager = $encryptionManager; $this->userManager = $userManager; $this->lockingProvider = $lockingProvider; $this->request = $request; $this->url = $url; } /** * @inheritdoc */ public function setupSettings(array $settings) { if (isset($settings[IManager::KEY_ADMIN_SECTION])) { $this->setupAdminSection($settings[IManager::KEY_ADMIN_SECTION]); } if (isset($settings[IManager::KEY_ADMIN_SETTINGS])) { $this->setupAdminSettings($settings[IManager::KEY_ADMIN_SETTINGS]); } } /** * attempts to remove an apps section and/or settings entry. A listener is * added centrally making sure that this method is called ones an app was * disabled. * * @param string $appId * @since 9.1.0 */ public function onAppDisabled($appId) { $appInfo = \OC_App::getAppInfo($appId); // hello static legacy if (isset($appInfo['settings'][IManager::KEY_ADMIN_SECTION])) { $this->mapper->remove(self::TABLE_ADMIN_SECTIONS, trim($appInfo['settings'][IManager::KEY_ADMIN_SECTION], '\\')); } if (isset($appInfo['settings'][IManager::KEY_ADMIN_SETTINGS])) { $this->mapper->remove(self::TABLE_ADMIN_SETTINGS, trim($appInfo['settings'][IManager::KEY_ADMIN_SETTINGS], '\\')); } } public function checkForOrphanedClassNames() { $tables = [self::TABLE_ADMIN_SECTIONS, self::TABLE_ADMIN_SETTINGS]; foreach ($tables as $table) { $classes = $this->mapper->getClasses($table); foreach ($classes as $className) { try { \OC::$server->query($className); } catch (QueryException $e) { $this->mapper->remove($table, $className); } } } } /** * @param string $sectionClassName */ private function setupAdminSection($sectionClassName) { if (!class_exists($sectionClassName)) { $this->log->debug('Could not find admin section class ' . $sectionClassName); return; } try { $section = $this->query($sectionClassName); } catch (QueryException $e) { // cancel return; } if (!$section instanceof ISection) { $this->log->error( 'Admin section instance must implement \OCP\ISection. Invalid class: {class}', ['class' => $sectionClassName] ); return; } if (!$this->hasAdminSection(get_class($section))) { $this->addAdminSection($section); } else { $this->updateAdminSection($section); } } private function addAdminSection(ISection $section) { $this->mapper->add(self::TABLE_ADMIN_SECTIONS, [ 'id' => $section->getID(), 'class' => get_class($section), 'priority' => $section->getPriority(), ]); } private function addAdminSettings(ISettings $settings) { $this->mapper->add(self::TABLE_ADMIN_SETTINGS, [ 'class' => get_class($settings), 'section' => $settings->getSection(), 'priority' => $settings->getPriority(), ]); } private function updateAdminSettings(ISettings $settings) { $this->mapper->update( self::TABLE_ADMIN_SETTINGS, 'class', get_class($settings), [ 'section' => $settings->getSection(), 'priority' => $settings->getPriority(), ] ); } private function updateAdminSection(ISection $section) { $this->mapper->update( self::TABLE_ADMIN_SECTIONS, 'class', get_class($section), [ 'id' => $section->getID(), 'priority' => $section->getPriority(), ] ); } /** * @param string $className * @return bool */ private function hasAdminSection($className) { return $this->mapper->has(self::TABLE_ADMIN_SECTIONS, $className); } /** * @param string $className * @return bool */ private function hasAdminSettings($className) { return $this->mapper->has(self::TABLE_ADMIN_SETTINGS, $className); } private function setupAdminSettings($settingsClassName) { if (!class_exists($settingsClassName)) { $this->log->debug('Could not find admin section class ' . $settingsClassName); return; } try { /** @var ISettings $settings */ $settings = $this->query($settingsClassName); } catch (QueryException $e) { // cancel return; } if (!$settings instanceof ISettings) { $this->log->error( 'Admin section instance must implement \OCP\Settings\ISection. Invalid class: {class}', ['class' => $settingsClassName] ); return; } if (!$this->hasAdminSettings(get_class($settings))) { $this->addAdminSettings($settings); } else { $this->updateAdminSettings($settings); } } private function query($className) { try { return \OC::$server->query($className); } catch (QueryException $e) { $this->log->logException($e); throw $e; } } /** * @inheritdoc */ public function getAdminSections() { // built-in sections $sections = [ 0 => [new Section('server', $this->l->t('Basic settings'), 0, $this->url->imagePath('settings', 'admin.svg'))], 5 => [new Section('sharing', $this->l->t('Sharing'), 0, $this->url->imagePath('core', 'actions/share.svg'))], 10 => [new Section('security', $this->l->t('Security'), 0, $this->url->imagePath('core', 'actions/password.svg'))], 45 => [new Section('encryption', $this->l->t('Encryption'), 0, $this->url->imagePath('core', 'actions/password.svg'))], 98 => [new Section('additional', $this->l->t('Additional settings'), 0, $this->url->imagePath('core', 'actions/settings-dark.svg'))], 99 => [new Section('tips-tricks', $this->l->t('Tips & tricks'), 0, $this->url->imagePath('settings', 'help.svg'))], ]; $rows = $this->mapper->getAdminSectionsFromDB(); foreach ($rows as $row) { if (!isset($sections[$row['priority']])) { $sections[$row['priority']] = []; } try { $sections[$row['priority']][] = $this->query($row['class']); } catch (QueryException $e) { // skip } } ksort($sections); return $sections; } /** * @param string $section * @return ISection[] */ private function getBuiltInAdminSettings($section) { $forms = []; try { if ($section === 'server') { /** @var ISettings $form */ $form = new Admin\Server($this->dbc, $this->request, $this->config, $this->lockingProvider, $this->l); $forms[$form->getPriority()] = [$form]; $form = new Admin\ServerDevNotice(); $forms[$form->getPriority()] = [$form]; } if ($section === 'encryption') { /** @var ISettings $form */ $form = new Admin\Encryption($this->encryptionManager, $this->userManager); $forms[$form->getPriority()] = [$form]; } if ($section === 'sharing') { /** @var ISettings $form */ $form = new Admin\Sharing($this->config); $forms[$form->getPriority()] = [$form]; } if ($section === 'additional') { /** @var ISettings $form */ $form = new Admin\Additional($this->config); $forms[$form->getPriority()] = [$form]; } if ($section === 'tips-tricks') { /** @var ISettings $form */ $form = new Admin\TipsTricks($this->config); $forms[$form->getPriority()] = [$form]; } } catch (QueryException $e) { // skip } return $forms; } /** * @inheritdoc */ public function getAdminSettings($section) { $settings = $this->getBuiltInAdminSettings($section); $dbRows = $this->mapper->getAdminSettingsFromDB($section); foreach ($dbRows as $row) { if (!isset($settings[$row['priority']])) { $settings[$row['priority']] = []; } try { $settings[$row['priority']][] = $this->query($row['class']); } catch (QueryException $e) { // skip } } ksort($settings); return $settings; } } private/Settings/RemoveOrphaned.php 0000604 00000004236 15247130452 0013451 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings; use OC\BackgroundJob\JobList; use OC\BackgroundJob\TimedJob; use OC\NeedsUpdateException; use OCP\BackgroundJob\IJobList; use OCP\ILogger; /** * Class RemoveOrphaned * * @package OC\Settings */ class RemoveOrphaned extends TimedJob { /** @var IJobList */ private $jobList; /** @var ILogger */ private $logger; /** @var Manager */ private $manager; public function __construct(Manager $manager = null) { if($manager !== null) { $this->manager = $manager; } else { // fix DI for Jobs $this->manager = \OC::$server->getSettingsManager(); } } /** * run the job, then remove it from the job list * * @param JobList $jobList * @param ILogger $logger */ public function execute($jobList, ILogger $logger = null) { // add an interval of 15 mins $this->setInterval(15*60); $this->jobList = $jobList; $this->logger = $logger; parent::execute($jobList, $logger); } /** * @param array $argument * @throws \Exception * @throws \OC\NeedsUpdateException */ protected function run($argument) { try { \OC_App::loadApps(); } catch (NeedsUpdateException $ex) { // only run when apps are up to date return; } $this->manager->checkForOrphanedClassNames(); // remove the job once executed successfully $this->jobList->remove($this); } } private/Settings/Section.php 0000604 00000004410 15247130452 0012131 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings; use OCP\Settings\IIconSection; class Section implements IIconSection { /** @var string */ private $id; /** @var string */ private $name; /** @var int */ private $priority; /** @var string */ private $icon; /** * @param string $id * @param string $name * @param int $priority * @param string $icon */ public function __construct($id, $name, $priority, $icon = '') { $this->id = $id; $this->name = $name; $this->priority = $priority; $this->icon = $icon; } /** * returns the ID of the section. It is supposed to be a lower case string, * e.g. 'ldap' * * @returns string */ public function getID() { return $this->id; } /** * returns the translated name as it should be displayed, e.g. 'LDAP / AD * integration'. Use the L10N service to translate it. * * @return string */ public function getName() { return $this->name; } /** * @return int whether the form should be rather on the top or bottom of * the settings navigation. The sections are arranged in ascending order of * the priority values. It is required to return a value between 0 and 99. * * E.g.: 70 */ public function getPriority() { return $this->priority; } /** * returns the relative path to an 16*16 icon describing the section. * e.g. '/core/img/places/files.svg' * * @returns string * @since 12 */ public function getIcon() { return $this->icon; } } private/Settings/Admin/Additional.php 0000604 00000005375 15247130452 0013640 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings\Admin; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\Settings\ISettings; class Additional implements ISettings { /** @var IConfig */ private $config; /** * @param IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * @return TemplateResponse */ public function getForm() { $parameters = [ // Mail 'sendmail_is_available' => (bool) \OC_Helper::findBinaryPath('sendmail'), 'mail_domain' => $this->config->getSystemValue('mail_domain', ''), 'mail_from_address' => $this->config->getSystemValue('mail_from_address', ''), 'mail_smtpmode' => $this->config->getSystemValue('mail_smtpmode', ''), 'mail_smtpsecure' => $this->config->getSystemValue('mail_smtpsecure', ''), 'mail_smtphost' => $this->config->getSystemValue('mail_smtphost', ''), 'mail_smtpport' => $this->config->getSystemValue('mail_smtpport', ''), 'mail_smtpauthtype' => $this->config->getSystemValue('mail_smtpauthtype', ''), 'mail_smtpauth' => $this->config->getSystemValue('mail_smtpauth', false), 'mail_smtpname' => $this->config->getSystemValue('mail_smtpname', ''), 'mail_smtppassword' => $this->config->getSystemValue('mail_smtppassword', ''), ]; if ($parameters['mail_smtppassword'] !== '') { $parameters['mail_smtppassword'] = '********'; } return new TemplateResponse('settings', 'admin/additional-mail', $parameters, ''); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection() { return 'additional'; } /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 */ public function getPriority() { return 0; } } private/Settings/Admin/TipsTricks.php 0000604 00000003600 15247130452 0013654 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings\Admin; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\Settings\ISettings; class TipsTricks implements ISettings { /** @var IConfig */ private $config; /** * @param IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * @return TemplateResponse */ public function getForm() { $databaseOverload = (strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false); $parameters = [ 'databaseOverload' => $databaseOverload, ]; return new TemplateResponse('settings', 'admin/tipstricks', $parameters, ''); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection() { return 'tips-tricks'; } /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 */ public function getPriority() { return 0; } } private/Settings/Admin/ServerDevNotice.php 0000604 00000003042 15247130452 0014624 0 ustar 00 <?php /** * @copyright 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings\Admin; use OCP\AppFramework\Http\TemplateResponse; use OCP\Settings\ISettings; class ServerDevNotice implements ISettings { /** * @return TemplateResponse */ public function getForm() { return new TemplateResponse('settings', 'admin/server.development.notice'); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection() { return 'server'; } /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 */ public function getPriority() { return 1000; } } private/Settings/Admin/Server.php 0000604 00000012510 15247130452 0013023 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings\Admin; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Platforms\SqlitePlatform; use OC\Lock\DBLockingProvider; use OC\Lock\NoopLockingProvider; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; use OCP\IRequest; use OCP\Lock\ILockingProvider; use OCP\Settings\ISettings; class Server implements ISettings { /** @var IDBConnection|Connection */ private $db; /** @var IRequest */ private $request; /** @var IConfig */ private $config; /** @var ILockingProvider */ private $lockingProvider; /** @var IL10N */ private $l; /** * @param IDBConnection $db * @param IRequest $request * @param IConfig $config * @param ILockingProvider $lockingProvider * @param IL10N $l */ public function __construct(IDBConnection $db, IRequest $request, IConfig $config, ILockingProvider $lockingProvider, IL10N $l) { $this->db = $db; $this->request = $request; $this->config = $config; $this->lockingProvider = $lockingProvider; $this->l = $l; } /** * @return TemplateResponse */ public function getForm() { try { if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) { $invalidTransactionIsolationLevel = false; } else { $invalidTransactionIsolationLevel = $this->db->getTransactionIsolation() !== Connection::TRANSACTION_READ_COMMITTED; } } catch (DBALException $e) { // ignore $invalidTransactionIsolationLevel = false; } $envPath = getenv('PATH'); // warn if outdated version of a memcache module is used $caches = [ 'apcu' => ['name' => $this->l->t('APCu'), 'version' => '4.0.6'], 'redis' => ['name' => $this->l->t('Redis'), 'version' => '2.2.5'], ]; $outdatedCaches = []; foreach ($caches as $php_module => $data) { $isOutdated = extension_loaded($php_module) && version_compare(phpversion($php_module), $data['version'], '<'); if ($isOutdated) { $outdatedCaches[$php_module] = $data; } } if ($this->lockingProvider instanceof NoopLockingProvider) { $fileLockingType = 'none'; } else if ($this->lockingProvider instanceof DBLockingProvider) { $fileLockingType = 'db'; } else { $fileLockingType = 'cache'; } $suggestedOverwriteCliUrl = ''; if ($this->config->getSystemValue('overwrite.cli.url', '') === '') { $suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT; if (!$this->config->getSystemValue('config_is_read_only', false)) { // Set the overwrite URL when it was not set yet. $this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl); $suggestedOverwriteCliUrl = ''; } } $parameters = [ // Diagnosis 'readOnlyConfigEnabled' => \OC_Helper::isReadOnlyConfigEnabled(), 'isLocaleWorking' => \OC_Util::isSetLocaleWorking(), 'isAnnotationsWorking' => \OC_Util::isAnnotationsWorking(), 'checkForWorkingWellKnownSetup' => $this->config->getSystemValue('check_for_working_wellknown_setup', true), 'has_fileinfo' => \OC_Util::fileInfoLoaded(), 'invalidTransactionIsolationLevel' => $invalidTransactionIsolationLevel, 'getenvServerNotWorking' => empty($envPath), 'OutdatedCacheWarning' => $outdatedCaches, 'fileLockingType' => $fileLockingType, 'suggestedOverwriteCliUrl' => $suggestedOverwriteCliUrl, // Background jobs 'backgroundjobs_mode' => $this->config->getAppValue('core', 'backgroundjobs_mode', 'ajax'), 'cron_log' => $this->config->getSystemValue('cron_log', true), 'lastcron' => $this->config->getAppValue('core', 'lastcron', false), 'cronErrors' => $this->config->getAppValue('core', 'cronErrors'), 'cli_based_cron_possible' => function_exists('posix_getpwuid'), 'cli_based_cron_user' => function_exists('posix_getpwuid') ? posix_getpwuid(fileowner(\OC::$configDir . 'config.php'))['name'] : '', ]; return new TemplateResponse('settings', 'admin/server', $parameters, ''); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection() { return 'server'; } /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 */ public function getPriority() { return 0; } } private/Settings/Admin/Sharing.php 0000604 00000007116 15247130452 0013156 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings\Admin; use OC\Share\Share; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\Settings\ISettings; use OCP\Util; class Sharing implements ISettings { /** @var IConfig */ private $config; /** * @param IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * @return TemplateResponse */ public function getForm() { $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); $excludeGroupsList = !is_null(json_decode($excludedGroups)) ? implode('|', json_decode($excludedGroups, true)) : ''; $parameters = [ // Built-In Sharing 'allowGroupSharing' => $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes'), 'allowLinks' => $this->config->getAppValue('core', 'shareapi_allow_links', 'yes'), 'allowPublicUpload' => $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes'), 'allowResharing' => $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes'), 'allowShareDialogUserEnumeration' => $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes'), 'enforceLinkPassword' => Util::isPublicLinkPasswordRequired(), 'onlyShareWithGroupMembers' => Share::shareWithGroupMembersOnly(), 'shareAPIEnabled' => $this->config->getAppValue('core', 'shareapi_enabled', 'yes'), 'shareDefaultExpireDateSet' => $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no'), 'shareExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'), 'shareEnforceExpireDate' => $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no'), 'shareExcludeGroups' => $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes' ? true : false, 'shareExcludedGroupsList' => $excludeGroupsList, 'publicShareDisclaimerText' => $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null), 'enableLinkPasswordByDefault' => $this->config->getAppValue('core', 'shareapi_enable_link_password_by_default', 'no'), ]; return new TemplateResponse('settings', 'admin/sharing', $parameters, ''); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection() { return 'sharing'; } /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 */ public function getPriority() { return 0; } } private/Settings/Admin/Encryption.php 0000604 00000005270 15247130452 0013714 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings\Admin; use OCP\AppFramework\Http\TemplateResponse; use OCP\Encryption\IManager; use OCP\IUserManager; use OCP\Settings\ISettings; class Encryption implements ISettings { /** @var IManager */ private $manager; /** @var IUserManager */ private $userManager; /** * @param IManager $manager * @param IUserManager $userManager */ public function __construct(IManager $manager, IUserManager $userManager) { $this->manager = $manager; $this->userManager = $userManager; } /** * @return TemplateResponse */ public function getForm() { $encryptionModules = $this->manager->getEncryptionModules(); $defaultEncryptionModuleId = $this->manager->getDefaultEncryptionModuleId(); $encryptionModuleList = []; foreach ($encryptionModules as $module) { $encryptionModuleList[$module['id']]['displayName'] = $module['displayName']; $encryptionModuleList[$module['id']]['default'] = false; if ($module['id'] === $defaultEncryptionModuleId) { $encryptionModuleList[$module['id']]['default'] = true; } } $parameters = [ // Encryption API 'encryptionEnabled' => $this->manager->isEnabled(), 'encryptionReady' => $this->manager->isReady(), 'externalBackendsEnabled' => count($this->userManager->getBackends()) > 1, // Modules 'encryptionModules' => $encryptionModuleList, ]; return new TemplateResponse('settings', 'admin/encryption', $parameters, ''); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection() { return 'encryption'; } /** * @return int whether the form should be rather on the top or bottom of * the admin section. The forms are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 */ public function getPriority() { return 0; } } private/Settings/Mapper.php 0000604 00000011041 15247130452 0011747 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> * * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Settings; use OCP\IDBConnection; class Mapper { const TABLE_ADMIN_SETTINGS = 'admin_settings'; const TABLE_ADMIN_SECTIONS = 'admin_sections'; /** @var IDBConnection */ private $dbc; /** * @param IDBConnection $dbc */ public function __construct(IDBConnection $dbc) { $this->dbc = $dbc; } /** * Get the configured admin settings from the database for the provided section * * @param string $section * @return array[] [['class' => string, 'priority' => int], ...] */ public function getAdminSettingsFromDB($section) { $query = $this->dbc->getQueryBuilder(); $query->select(['class', 'priority']) ->from(self::TABLE_ADMIN_SETTINGS) ->where($query->expr()->eq('section', $this->dbc->getQueryBuilder()->createParameter('section'))) ->setParameter('section', $section); $result = $query->execute(); return $result->fetchAll(); } /** * Get the configured admin sections from the database * * @return array[] [['class' => string, 'priority' => int], ...] */ public function getAdminSectionsFromDB() { $query = $this->dbc->getQueryBuilder(); $query->selectDistinct('s.class') ->addSelect('s.priority') ->from(self::TABLE_ADMIN_SECTIONS, 's') ->from(self::TABLE_ADMIN_SETTINGS, 'f') ->where($query->expr()->eq('s.id', 'f.section')); $result = $query->execute(); return array_map(function ($row) { $row['priority'] = (int)$row['priority']; return $row; }, $result->fetchAll()); } /** * @param string $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS * @param array $values */ public function add($table, array $values) { $query = $this->dbc->getQueryBuilder(); $values = array_map(function ($value) use ($query) { return $query->createNamedParameter($value); }, $values); $query->insert($table)->values($values); $query->execute(); } /** * returns the registered classes in the given table * * @param $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS * @return string[] */ public function getClasses($table) { $q = $this->dbc->getQueryBuilder(); $resultStatement = $q->select('class') ->from($table) ->execute(); $data = $resultStatement->fetchAll(); $resultStatement->closeCursor(); return array_map(function ($row) { return $row['class']; }, $data); } /** * Check if a class is configured in the database * * @param string $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS * @param string $className * @return bool */ public function has($table, $className) { $query = $this->dbc->getQueryBuilder(); $query->select('class') ->from($table) ->where($query->expr()->eq('class', $query->createNamedParameter($className))) ->setMaxResults(1); $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); return (bool)$row; } /** * deletes an settings or admin entry from the given table * * @param $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS * @param $className */ public function remove($table, $className) { $query = $this->dbc->getQueryBuilder(); $query->delete($table) ->where($query->expr()->eq('class', $query->createNamedParameter($className))); $query->execute(); } /** * @param $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS * @param $idCol * @param $id * @param $values */ public function update($table, $idCol, $id, $values) { $query = $this->dbc->getQueryBuilder(); $query->update($table); foreach ($values as $key => $value) { $query->set($key, $query->createNamedParameter($value)); } $query ->where($query->expr()->eq($idCol, $query->createParameter($idCol))) ->setParameter($idCol, $id) ->execute(); } } private/Lock/MemcacheLockingProvider.php 0000604 00000007021 15247130452 0014342 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Lock; use OCP\IMemcacheTTL; use OCP\Lock\LockedException; use OCP\IMemcache; class MemcacheLockingProvider extends AbstractLockingProvider { /** * @var \OCP\IMemcache */ private $memcache; /** * @param \OCP\IMemcache $memcache * @param int $ttl */ public function __construct(IMemcache $memcache, $ttl = 3600) { $this->memcache = $memcache; $this->ttl = $ttl; } private function setTTL($path) { if ($this->memcache instanceof IMemcacheTTL) { $this->memcache->setTTL($path, $this->ttl); } } /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @return bool */ public function isLocked($path, $type) { $lockValue = $this->memcache->get($path); if ($type === self::LOCK_SHARED) { return $lockValue > 0; } else if ($type === self::LOCK_EXCLUSIVE) { return $lockValue === 'exclusive'; } else { return false; } } /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException */ public function acquireLock($path, $type) { if ($type === self::LOCK_SHARED) { if (!$this->memcache->inc($path)) { throw new LockedException($path); } } else { $this->memcache->add($path, 0); if (!$this->memcache->cas($path, 0, 'exclusive')) { throw new LockedException($path); } } $this->setTTL($path); $this->markAcquire($path, $type); } /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE */ public function releaseLock($path, $type) { if ($type === self::LOCK_SHARED) { if ($this->getOwnSharedLockCount($path) === 1) { $removed = $this->memcache->cad($path, 1); // if we're the only one having a shared lock we can remove it in one go if (!$removed) { //someone else also has a shared lock, decrease only $this->memcache->dec($path); } } else { // if we own more than one lock ourselves just decrease $this->memcache->dec($path); } } else if ($type === self::LOCK_EXCLUSIVE) { $this->memcache->cad($path, 'exclusive'); } $this->markRelease($path, $type); } /** * Change the type of an existing lock * * @param string $path * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException */ public function changeLock($path, $targetType) { if ($targetType === self::LOCK_SHARED) { if (!$this->memcache->cas($path, 'exclusive', 1)) { throw new LockedException($path); } } else if ($targetType === self::LOCK_EXCLUSIVE) { // we can only change a shared lock to an exclusive if there's only a single owner of the shared lock if (!$this->memcache->cas($path, 1, 'exclusive')) { throw new LockedException($path); } } $this->setTTL($path); $this->markChange($path, $targetType); } } private/Lock/NoopLockingProvider.php 0000604 00000002706 15247130452 0013560 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Lock; use OCP\Lock\ILockingProvider; /** * Locking provider that does nothing. * * To be used when locking is disabled. */ class NoopLockingProvider implements ILockingProvider { /** * {@inheritdoc} */ public function isLocked($path, $type) { return false; } /** * {@inheritdoc} */ public function acquireLock($path, $type) { // do nothing } /** * {@inheritdoc} */ public function releaseLock($path, $type) { // do nothing } /**1 * {@inheritdoc} */ public function releaseAll() { // do nothing } /** * {@inheritdoc} */ public function changeLock($path, $targetType) { // do nothing } } private/Lock/AbstractLockingProvider.php 0000604 00000007402 15247130452 0014406 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Lock; use OCP\Lock\ILockingProvider; /** * Base locking provider that keeps track of locks acquired during the current request * to release any left over locks at the end of the request */ abstract class AbstractLockingProvider implements ILockingProvider { protected $ttl; // how long until we clear stray locks in seconds protected $acquiredLocks = [ 'shared' => [], 'exclusive' => [] ]; /** * Check if we've locally acquired a lock * * @param string $path * @param int $type * @return bool */ protected function hasAcquiredLock($path, $type) { if ($type === self::LOCK_SHARED) { return isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 0; } else { return isset($this->acquiredLocks['exclusive'][$path]) && $this->acquiredLocks['exclusive'][$path] === true; } } /** * Mark a locally acquired lock * * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE */ protected function markAcquire($path, $type) { if ($type === self::LOCK_SHARED) { if (!isset($this->acquiredLocks['shared'][$path])) { $this->acquiredLocks['shared'][$path] = 0; } $this->acquiredLocks['shared'][$path]++; } else { $this->acquiredLocks['exclusive'][$path] = true; } } /** * Mark a release of a locally acquired lock * * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE */ protected function markRelease($path, $type) { if ($type === self::LOCK_SHARED) { if (isset($this->acquiredLocks['shared'][$path]) and $this->acquiredLocks['shared'][$path] > 0) { $this->acquiredLocks['shared'][$path]--; if ($this->acquiredLocks['shared'][$path] === 0) { unset($this->acquiredLocks['shared'][$path]); } } } else if ($type === self::LOCK_EXCLUSIVE) { unset($this->acquiredLocks['exclusive'][$path]); } } /** * Change the type of an existing tracked lock * * @param string $path * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE */ protected function markChange($path, $targetType) { if ($targetType === self::LOCK_SHARED) { unset($this->acquiredLocks['exclusive'][$path]); if (!isset($this->acquiredLocks['shared'][$path])) { $this->acquiredLocks['shared'][$path] = 0; } $this->acquiredLocks['shared'][$path]++; } else if ($targetType === self::LOCK_EXCLUSIVE) { $this->acquiredLocks['exclusive'][$path] = true; $this->acquiredLocks['shared'][$path]--; } } /** * release all lock acquired by this instance which were marked using the mark* methods */ public function releaseAll() { foreach ($this->acquiredLocks['shared'] as $path => $count) { for ($i = 0; $i < $count; $i++) { $this->releaseLock($path, self::LOCK_SHARED); } } foreach ($this->acquiredLocks['exclusive'] as $path => $hasLock) { $this->releaseLock($path, self::LOCK_EXCLUSIVE); } } protected function getOwnSharedLockCount($path) { return isset($this->acquiredLocks['shared'][$path]) ? $this->acquiredLocks['shared'][$path] : 0; } } private/Lock/DBLockingProvider.php 0000604 00000017417 15247130452 0013137 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Individual IT Services <info@individual-it.net> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Lock; use OC\DB\QueryBuilder\Literal; use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\ILogger; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; /** * Locking provider that stores the locks in the database */ class DBLockingProvider extends AbstractLockingProvider { /** * @var \OCP\IDBConnection */ private $connection; /** * @var \OCP\ILogger */ private $logger; /** * @var \OCP\AppFramework\Utility\ITimeFactory */ private $timeFactory; private $sharedLocks = []; /** * Check if we have an open shared lock for a path * * @param string $path * @return bool */ protected function isLocallyLocked($path) { return isset($this->sharedLocks[$path]) && $this->sharedLocks[$path]; } /** * Mark a locally acquired lock * * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE */ protected function markAcquire($path, $type) { parent::markAcquire($path, $type); if ($type === self::LOCK_SHARED) { $this->sharedLocks[$path] = true; } } /** * Change the type of an existing tracked lock * * @param string $path * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE */ protected function markChange($path, $targetType) { parent::markChange($path, $targetType); if ($targetType === self::LOCK_SHARED) { $this->sharedLocks[$path] = true; } else if ($targetType === self::LOCK_EXCLUSIVE) { $this->sharedLocks[$path] = false; } } /** * @param \OCP\IDBConnection $connection * @param \OCP\ILogger $logger * @param \OCP\AppFramework\Utility\ITimeFactory $timeFactory * @param int $ttl */ public function __construct(IDBConnection $connection, ILogger $logger, ITimeFactory $timeFactory, $ttl = 3600) { $this->connection = $connection; $this->logger = $logger; $this->timeFactory = $timeFactory; $this->ttl = $ttl; } /** * Insert a file locking row if it does not exists. * * @param string $path * @param int $lock * @return int number of inserted rows */ protected function initLockField($path, $lock = 0) { $expire = $this->getExpireTime(); return $this->connection->insertIfNotExist('*PREFIX*file_locks', ['key' => $path, 'lock' => $lock, 'ttl' => $expire], ['key']); } /** * @return int */ protected function getExpireTime() { return $this->timeFactory->getTime() + $this->ttl; } /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @return bool */ public function isLocked($path, $type) { if ($this->hasAcquiredLock($path, $type)) { return true; } $query = $this->connection->prepare('SELECT `lock` from `*PREFIX*file_locks` WHERE `key` = ?'); $query->execute([$path]); $lockValue = (int)$query->fetchColumn(); if ($type === self::LOCK_SHARED) { if ($this->isLocallyLocked($path)) { // if we have a shared lock we kept open locally but it's released we always have at least 1 shared lock in the db return $lockValue > 1; } else { return $lockValue > 0; } } else if ($type === self::LOCK_EXCLUSIVE) { return $lockValue === -1; } else { return false; } } /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException */ public function acquireLock($path, $type) { $expire = $this->getExpireTime(); if ($type === self::LOCK_SHARED) { if (!$this->isLocallyLocked($path)) { $result = $this->initLockField($path, 1); if ($result <= 0) { $result = $this->connection->executeUpdate( 'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0', [$expire, $path] ); } } else { $result = 1; } } else { $existing = 0; if ($this->hasAcquiredLock($path, ILockingProvider::LOCK_SHARED) === false && $this->isLocallyLocked($path)) { $existing = 1; } $result = $this->initLockField($path, -1); if ($result <= 0) { $result = $this->connection->executeUpdate( 'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = ?', [$expire, $path, $existing] ); } } if ($result !== 1) { throw new LockedException($path); } $this->markAcquire($path, $type); } /** * @param string $path * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE */ public function releaseLock($path, $type) { $this->markRelease($path, $type); // we keep shared locks till the end of the request so we can re-use them if ($type === self::LOCK_EXCLUSIVE) { $this->connection->executeUpdate( 'UPDATE `*PREFIX*file_locks` SET `lock` = 0 WHERE `key` = ? AND `lock` = -1', [$path] ); } } /** * Change the type of an existing lock * * @param string $path * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException */ public function changeLock($path, $targetType) { $expire = $this->getExpireTime(); if ($targetType === self::LOCK_SHARED) { $result = $this->connection->executeUpdate( 'UPDATE `*PREFIX*file_locks` SET `lock` = 1, `ttl` = ? WHERE `key` = ? AND `lock` = -1', [$expire, $path] ); } else { // since we only keep one shared lock in the db we need to check if we have more then one shared lock locally manually if (isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 1) { throw new LockedException($path); } $result = $this->connection->executeUpdate( 'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 1', [$expire, $path] ); } if ($result !== 1) { throw new LockedException($path); } $this->markChange($path, $targetType); } /** * cleanup empty locks */ public function cleanExpiredLocks() { $expire = $this->timeFactory->getTime(); try { $this->connection->executeUpdate( 'DELETE FROM `*PREFIX*file_locks` WHERE `ttl` < ?', [$expire] ); } catch (\Exception $e) { // If the table is missing, the clean up was successful if ($this->connection->tableExists('file_locks')) { throw $e; } } } /** * release all lock acquired by this instance which were marked using the mark* methods */ public function releaseAll() { parent::releaseAll(); // since we keep shared locks we need to manually clean those $lockedPaths = array_keys($this->sharedLocks); $lockedPaths = array_filter($lockedPaths, function ($path) { return $this->sharedLocks[$path]; }); $chunkedPaths = array_chunk($lockedPaths, 100); foreach ($chunkedPaths as $chunk) { $builder = $this->connection->getQueryBuilder(); $query = $builder->update('file_locks') ->set('lock', $builder->createFunction('`lock` -1')) ->where($builder->expr()->in('key', $builder->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY))) ->andWhere($builder->expr()->gt('lock', new Literal(0))); $query->execute(); } } } private/AppConfig.php 0000604 00000020075 15247130452 0010600 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com> * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\DB\OracleConnection; use OCP\IAppConfig; use OCP\IConfig; use OCP\IDBConnection; /** * This class provides an easy way for apps to store config values in the * database. */ class AppConfig implements IAppConfig { /** @var array[] */ protected $sensitiveValues = [ 'spreed' => [ 'turn_server_secret', ], 'user_ldap' => [ 'ldap_agent_password', ], ]; /** @var \OCP\IDBConnection */ protected $conn; /** @var array[] */ private $cache = []; /** @var bool */ private $configLoaded = false; /** * @param IDBConnection $conn */ public function __construct(IDBConnection $conn) { $this->conn = $conn; $this->configLoaded = false; } /** * @param string $app * @return array */ private function getAppValues($app) { $this->loadConfigValues(); if (isset($this->cache[$app])) { return $this->cache[$app]; } return []; } /** * Get all apps using the config * * @return array an array of app ids * * This function returns a list of all apps that have at least one * entry in the appconfig table. */ public function getApps() { $this->loadConfigValues(); return $this->getSortedKeys($this->cache); } /** * Get the available keys for an app * * @param string $app the app we are looking for * @return array an array of key names * @deprecated 8.0.0 use method getAppKeys of \OCP\IConfig * * This function gets all keys of an app. Please note that the values are * not returned. */ public function getKeys($app) { $this->loadConfigValues(); if (isset($this->cache[$app])) { return $this->getSortedKeys($this->cache[$app]); } return []; } public function getSortedKeys($data) { $keys = array_keys($data); sort($keys); return $keys; } /** * Gets the config value * * @param string $app app * @param string $key key * @param string $default = null, default value if the key does not exist * @return string the value or $default * @deprecated 8.0.0 use method getAppValue of \OCP\IConfig * * This function gets a value from the appconfig table. If the key does * not exist the default value will be returned */ public function getValue($app, $key, $default = null) { $this->loadConfigValues(); if ($this->hasKey($app, $key)) { return $this->cache[$app][$key]; } return $default; } /** * check if a key is set in the appconfig * * @param string $app * @param string $key * @return bool */ public function hasKey($app, $key) { $this->loadConfigValues(); return isset($this->cache[$app][$key]); } /** * Sets a value. If the key did not exist before it will be created. * * @param string $app app * @param string $key key * @param string|float|int $value value * @return bool True if the value was inserted or updated, false if the value was the same * @deprecated 8.0.0 use method setAppValue of \OCP\IConfig */ public function setValue($app, $key, $value) { if (!$this->hasKey($app, $key)) { $inserted = (bool) $this->conn->insertIfNotExist('*PREFIX*appconfig', [ 'appid' => $app, 'configkey' => $key, 'configvalue' => $value, ], [ 'appid', 'configkey', ]); if ($inserted) { if (!isset($this->cache[$app])) { $this->cache[$app] = []; } $this->cache[$app][$key] = $value; return true; } } $sql = $this->conn->getQueryBuilder(); $sql->update('appconfig') ->set('configvalue', $sql->createParameter('configvalue')) ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey'))) ->setParameter('configvalue', $value) ->setParameter('app', $app) ->setParameter('configkey', $key); /* * Only limit to the existing value for non-Oracle DBs: * http://docs.oracle.com/cd/E11882_01/server.112/e26088/conditions002.htm#i1033286 * > Large objects (LOBs) are not supported in comparison conditions. */ if (!($this->conn instanceof OracleConnection)) { // Only update the value when it is not the same $sql->andWhere($sql->expr()->neq('configvalue', $sql->createParameter('configvalue'))) ->setParameter('configvalue', $value); } $changedRow = (bool) $sql->execute(); $this->cache[$app][$key] = $value; return $changedRow; } /** * Deletes a key * * @param string $app app * @param string $key key * @return boolean * @deprecated 8.0.0 use method deleteAppValue of \OCP\IConfig */ public function deleteKey($app, $key) { $this->loadConfigValues(); $sql = $this->conn->getQueryBuilder(); $sql->delete('appconfig') ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey'))) ->setParameter('app', $app) ->setParameter('configkey', $key); $sql->execute(); unset($this->cache[$app][$key]); return false; } /** * Remove app from appconfig * * @param string $app app * @return boolean * @deprecated 8.0.0 use method deleteAppValue of \OCP\IConfig * * Removes all keys in appconfig belonging to the app. */ public function deleteApp($app) { $this->loadConfigValues(); $sql = $this->conn->getQueryBuilder(); $sql->delete('appconfig') ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) ->setParameter('app', $app); $sql->execute(); unset($this->cache[$app]); return false; } /** * get multiple values, either the app or key can be used as wildcard by setting it to false * * @param string|false $app * @param string|false $key * @return array|false */ public function getValues($app, $key) { if (($app !== false) === ($key !== false)) { return false; } if ($key === false) { return $this->getAppValues($app); } else { $appIds = $this->getApps(); $values = array_map(function($appId) use ($key) { return isset($this->cache[$appId][$key]) ? $this->cache[$appId][$key] : null; }, $appIds); $result = array_combine($appIds, $values); return array_filter($result); } } /** * get all values of the app or and filters out sensitive data * * @param string $app * @return array */ public function getFilteredValues($app) { $values = $this->getValues($app, false); foreach ($this->sensitiveValues[$app] as $sensitiveKey) { if (isset($values[$sensitiveKey])) { $values[$sensitiveKey] = IConfig::SENSITIVE_VALUE; } } return $values; } /** * Load all the app config values */ protected function loadConfigValues() { if ($this->configLoaded) { return; } $this->cache = []; $sql = $this->conn->getQueryBuilder(); $sql->select('*') ->from('appconfig'); $result = $sql->execute(); // we are going to store the result in memory anyway $rows = $result->fetchAll(); foreach ($rows as $row) { if (!isset($this->cache[$row['appid']])) { $this->cache[$row['appid']] = []; } $this->cache[$row['appid']][$row['configkey']] = $row['configvalue']; } $result->closeCursor(); $this->configLoaded = true; } } private/GlobalScale/Config.php 0000604 00000003357 15247130452 0012313 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\GlobalScale; use OCP\IConfig; class Config implements \OCP\GlobalScale\IConfig { /** @var IConfig */ private $config; /** * Config constructor. * * @param IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * check if global scale is enabled * * @since 12.0.1 * @return bool */ public function isGlobalScaleEnabled() { $enabled = $this->config->getSystemValue('gs.enabled', false); return $enabled !== false; } /** * check if federation should only be used internally in a global scale setup * * @since 12.0.1 * @return bool */ public function onlyInternalFederation() { // if global scale is disabled federation works always globally $gsEnabled = $this->isGlobalScaleEnabled(); if ($gsEnabled === false) { return false; } $enabled = $this->config->getSystemValue('gs.federation', 'internal'); return $enabled === 'internal'; } } private/Repair/Collation.php 0000604 00000010500 15247130452 0012070 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Repair; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Platforms\MySqlPlatform; use OCP\IConfig; use OCP\IDBConnection; use OCP\ILogger; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class Collation implements IRepairStep { /** @var IConfig */ protected $config; /** @var ILogger */ protected $logger; /** @var IDBConnection */ protected $connection; /** @var bool */ protected $ignoreFailures; /** * @param IConfig $config * @param ILogger $logger * @param IDBConnection $connection * @param bool $ignoreFailures */ public function __construct(IConfig $config, ILogger $logger, IDBConnection $connection, $ignoreFailures) { $this->connection = $connection; $this->config = $config; $this->logger = $logger; $this->ignoreFailures = $ignoreFailures; } public function getName() { return 'Repair MySQL collation'; } /** * Fix mime types */ public function run(IOutput $output) { if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) { $output->info('Not a mysql database -> nothing to do'); return; } $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8'; $tables = $this->getAllNonUTF8BinTables($this->connection); foreach ($tables as $table) { $output->info("Change row format for $table ..."); $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;'); try { $query->execute(); } catch (DriverException $e) { // Just log this $this->logger->logException($e); if (!$this->ignoreFailures) { throw $e; } } $output->info("Change collation for $table ..."); if ($characterSet === 'utf8mb4') { // need to set row compression first $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT=COMPRESSED;'); $query->execute(); } $query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;'); try { $query->execute(); } catch (DriverException $e) { // Just log this $this->logger->logException($e); if (!$this->ignoreFailures) { throw $e; } } } if (empty($tables)) { $output->info('All tables already have the correct collation -> nothing to do'); } } /** * @param IDBConnection $connection * @return string[] */ protected function getAllNonUTF8BinTables(IDBConnection $connection) { $dbName = $this->config->getSystemValue("dbname"); $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8'; // fetch tables by columns $statement = $connection->executeQuery( "SELECT DISTINCT(TABLE_NAME) AS `table`" . " FROM INFORMATION_SCHEMA . COLUMNS" . " WHERE TABLE_SCHEMA = ?" . " AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')" . " AND TABLE_NAME LIKE \"*PREFIX*%\"", array($dbName) ); $rows = $statement->fetchAll(); $result = []; foreach ($rows as $row) { $result[$row['table']] = true; } // fetch tables by collation $statement = $connection->executeQuery( "SELECT DISTINCT(TABLE_NAME) AS `table`" . " FROM INFORMATION_SCHEMA . TABLES" . " WHERE TABLE_SCHEMA = ?" . " AND TABLE_COLLATION <> '" . $characterSet . "_bin'" . " AND TABLE_NAME LIKE \"*PREFIX*%\"", [$dbName] ); $rows = $statement->fetchAll(); foreach ($rows as $row) { $result[$row['table']] = true; } return array_keys($result); } } private/Repair/SqliteAutoincrement.php 0000604 00000005563 15247130452 0014160 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Repair; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\SchemaDiff; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Schema\ColumnDiff; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; /** * Fixes Sqlite autoincrement by forcing the SQLite table schemas to be * altered in order to retrigger SQL schema generation through OCSqlitePlatform. */ class SqliteAutoincrement implements IRepairStep { /** * @var \OC\DB\Connection */ protected $connection; /** * @param \OC\DB\Connection $connection */ public function __construct($connection) { $this->connection = $connection; } public function getName() { return 'Repair SQLite autoincrement'; } /** * Fix mime types */ public function run(IOutput $out) { if (!$this->connection->getDatabasePlatform() instanceof SqlitePlatform) { return; } $sourceSchema = $this->connection->getSchemaManager()->createSchema(); $schemaDiff = new SchemaDiff(); foreach ($sourceSchema->getTables() as $tableSchema) { $primaryKey = $tableSchema->getPrimaryKey(); if (!$primaryKey) { continue; } $columnNames = $primaryKey->getColumns(); // add a column diff for every primary key column, // but do not actually change anything, this will // force the generation of SQL statements to alter // those tables, which will then trigger the // specific SQL code from OCSqlitePlatform try { $tableDiff = new TableDiff($tableSchema->getName()); $tableDiff->fromTable = $tableSchema; foreach ($columnNames as $columnName) { $columnSchema = $tableSchema->getColumn($columnName); $columnDiff = new ColumnDiff($columnSchema->getName(), $columnSchema); $tableDiff->changedColumns[] = $columnDiff; $schemaDiff->changedTables[] = $tableDiff; } } catch (SchemaException $e) { // ignore } } $this->connection->beginTransaction(); foreach ($schemaDiff->toSql($this->connection->getDatabasePlatform()) as $sql) { $this->connection->query($sql); } $this->connection->commit(); } } private/Repair/NC11/CleanPreviews.php 0000604 00000003725 15247130452 0013370 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC11; use OCP\BackgroundJob\IJobList; use OCP\IConfig; use OCP\IUser; use OCP\IUserManager; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class CleanPreviews implements IRepairStep { /** @var IJobList */ private $jobList; /** @var IUserManager */ private $userManager; /** @var IConfig */ private $config; /** * MoveAvatars constructor. * * @param IJobList $jobList * @param IUserManager $userManager * @param IConfig $config */ public function __construct(IJobList $jobList, IUserManager $userManager, IConfig $config) { $this->jobList = $jobList; $this->userManager = $userManager; $this->config = $config; } /** * @return string */ public function getName() { return 'Add preview cleanup background jobs'; } public function run(IOutput $output) { if (!$this->config->getAppValue('core', 'previewsCleanedUp', false)) { $this->userManager->callForSeenUsers(function (IUser $user) { $this->jobList->add(CleanPreviewsBackgroundJob::class, ['uid' => $user->getUID()]); }); $this->config->setAppValue('core', 'previewsCleanedUp', 1); } } } private/Repair/NC11/MoveAvatars.php 0000604 00000003746 15247130452 0013054 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC11; use OCP\BackgroundJob\IJobList; use OCP\IConfig; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class MoveAvatars implements IRepairStep { /** @var IJobList */ private $jobList; /** @var IConfig */ private $config; /** * MoveAvatars constructor. * * @param IJobList $jobList * @param IConfig $config */ public function __construct(IJobList $jobList, IConfig $config) { $this->jobList = $jobList; $this->config = $config; } /** * @return string */ public function getName() { return 'Add move avatar background job'; } public function run(IOutput $output) { // only run once if ($this->config->getAppValue('core', 'moveavatarsdone') === 'yes') { $output->info('Repair step already executed'); return; } if ($this->config->getSystemValue('enable_avatars', true) === false) { $output->info('Avatars are disabled'); } else { $output->info('Add background job'); $this->jobList->add(MoveAvatarsBackgroundJob::class); // if all were done, no need to redo the repair during next upgrade $this->config->setAppValue('core', 'moveavatarsdone', 'yes'); } } } private/Repair/NC11/CleanPreviewsBackgroundJob.php 0000604 00000005745 15247130452 0016027 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC11; use OC\BackgroundJob\QueuedJob; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\ILogger; class CleanPreviewsBackgroundJob extends QueuedJob { /** @var IRootFolder */ private $rootFolder; /** @var ILogger */ private $logger; /** @var IJobList */ private $jobList; /** @var ITimeFactory */ private $timeFactory; /** * CleanPreviewsBackgroundJob constructor. * * @param IRootFolder $rootFolder * @param ILogger $logger * @param IJobList $jobList * @param ITimeFactory $timeFactory */ public function __construct(IRootFolder $rootFolder, ILogger $logger, IJobList $jobList, ITimeFactory $timeFactory) { $this->rootFolder = $rootFolder; $this->logger = $logger; $this->jobList = $jobList; $this->timeFactory = $timeFactory; } public function run($arguments) { $uid = $arguments['uid']; $this->logger->info('Started preview cleanup for ' . $uid); $empty = $this->cleanupPreviews($uid); if (!$empty) { $this->jobList->add(self::class, ['uid' => $uid]); $this->logger->info('New preview cleanup scheduled for ' . $uid); } else { $this->logger->info('Preview cleanup done for ' . $uid); } } /** * @param $uid * @return bool */ private function cleanupPreviews($uid) { try { $userFolder = $this->rootFolder->getUserFolder($uid); } catch (NotFoundException $e) { return true; } $userRoot = $userFolder->getParent(); try { /** @var Folder $thumbnailFolder */ $thumbnailFolder = $userRoot->get('thumbnails'); } catch (NotFoundException $e) { return true; } $thumbnails = $thumbnailFolder->getDirectoryListing(); $start = $this->timeFactory->getTime(); foreach ($thumbnails as $thumbnail) { try { $thumbnail->delete(); } catch (NotPermittedException $e) { // Ignore } if (($this->timeFactory->getTime() - $start) > 15) { return false; } } try { $thumbnailFolder->delete(); } catch (NotPermittedException $e) { // Ignore } return true; } } private/Repair/NC11/MoveAvatarsBackgroundJob.php 0000604 00000007724 15247130452 0015507 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC11; use OC\BackgroundJob\QueuedJob; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IAppData; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\ILogger; use OCP\IUser; use OCP\IUserManager; class MoveAvatarsBackgroundJob extends QueuedJob { /** @var IUserManager */ private $userManager; /** @var IRootFolder */ private $rootFolder; /** @var IAppData */ private $appData; /** @var ILogger */ private $logger; /** * MoveAvatars constructor. */ public function __construct() { $this->userManager = \OC::$server->getUserManager(); $this->rootFolder = \OC::$server->getRootFolder(); $this->logger = \OC::$server->getLogger(); $this->appData = \OC::$server->getAppDataDir('avatar'); } public function run($arguments) { $this->logger->info('Started migrating avatars to AppData folder'); $this->moveAvatars(); $this->logger->info('All avatars migrated to AppData folder'); } private function moveAvatars() { try { $ownCloudAvatars = $this->rootFolder->get('avatars'); } catch (NotFoundException $e) { $ownCloudAvatars = null; } $counter = 0; $this->userManager->callForSeenUsers(function (IUser $user) use ($counter, $ownCloudAvatars) { $uid = $user->getUID(); \OC\Files\Filesystem::initMountPoints($uid); /** @var Folder $userFolder */ $userFolder = $this->rootFolder->get($uid); try { $userData = $this->appData->getFolder($uid); } catch (NotFoundException $e) { $userData = $this->appData->newFolder($uid); } $foundAvatars = $this->copyAvatarsFromFolder($userFolder, $userData); // ownCloud migration? if ($foundAvatars === 0 && $ownCloudAvatars instanceof Folder) { $parts = $this->buildOwnCloudAvatarPath($uid); $userOwnCloudAvatar = $ownCloudAvatars; foreach ($parts as $part) { try { $userOwnCloudAvatar = $userOwnCloudAvatar->get($part); } catch (NotFoundException $e) { return; } } $this->copyAvatarsFromFolder($userOwnCloudAvatar, $userData); } $counter++; if ($counter % 100 === 0) { $this->logger->info('{amount} avatars migrated', ['amount' => $counter]); } }); } /** * @param Folder $source * @param ISimpleFolder $target * @return int * @throws \OCP\Files\NotPermittedException * @throws NotFoundException */ protected function copyAvatarsFromFolder(Folder $source, ISimpleFolder $target) { $foundAvatars = 0; $avatars = $source->getDirectoryListing(); $regex = '/^avatar\.([0-9]+\.)?(jpg|png)$/'; foreach ($avatars as $avatar) { /** @var File $avatar */ if (preg_match($regex, $avatar->getName())) { /* * This is not the most effective but it is the most abstract way * to handle this. Avatars should be small anyways. */ $newAvatar = $target->newFile($avatar->getName()); $newAvatar->putContent($avatar->getContent()); $avatar->delete(); $foundAvatars++; } } return $foundAvatars; } protected function buildOwnCloudAvatarPath($userId) { $avatar = substr_replace(substr_replace(md5($userId), '/', 4, 0), '/', 2, 0); return explode('/', $avatar); } } private/Repair/NC11/FixMountStorages.php 0000604 00000004320 15247130452 0014072 0 ustar 00 <?php /** * @copyright 2016 Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC11; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class FixMountStorages implements IRepairStep { /** @var IDBConnection */ private $db; /** * @param IDBConnection $db */ public function __construct(IDBConnection $db) { $this->db = $db; } /** * @return string */ public function getName() { return 'Fix potential broken mount points'; } public function run(IOutput $output) { $query = $this->db->getQueryBuilder(); $query->select('m.id', 'f.storage') ->from('mounts', 'm') ->leftJoin('m', 'filecache', 'f', $query->expr()->eq('m.root_id', 'f.fileid')) ->where($query->expr()->neq('m.storage_id', 'f.storage')); $update = $this->db->getQueryBuilder(); $update->update('mounts') ->set('storage_id', $update->createParameter('storage')) ->where($query->expr()->eq('id', $update->createParameter('mount'))); $result = $query->execute(); $entriesUpdated = 0; while ($row = $result->fetch()) { $update->setParameter('storage', $row['storage'], IQueryBuilder::PARAM_INT) ->setParameter('mount', $row['id'], IQueryBuilder::PARAM_INT); $update->execute(); $entriesUpdated++; } $result->closeCursor(); if ($entriesUpdated > 0) { $output->info($entriesUpdated . ' mounts updated'); return; } $output->info('No mounts updated'); } } private/Repair/NC12/RepairIdentityProofKeyFolders.php 0000604 00000005635 15247130452 0016556 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC12; use OC\Files\AppData\Factory; use OCP\Files\IRootFolder; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IConfig; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class RepairIdentityProofKeyFolders implements IRepairStep { /** @var IConfig */ private $config; /** @var \OC\Files\AppData\AppData */ private $appDataIdentityProof; /** @var IRootFolder */ private $rootFolder; /** @var string */ private $identityProofDir; /** * RepairIdentityProofKeyFolders constructor. * * @param IConfig $config * @param Factory $appDataFactory * @param IRootFolder $rootFolder */ public function __construct(IConfig $config, Factory $appDataFactory, IRootFolder $rootFolder) { $this->config = $config; $this->appDataIdentityProof = $appDataFactory->get('identityproof'); $this->rootFolder = $rootFolder; $instanceId = $this->config->getSystemValue('instanceid', null); if ($instanceId === null) { throw new \RuntimeException('no instance id!'); } $this->identityProofDir = 'appdata_' . $instanceId . '/identityproof/'; } /** * Returns the step's name * * @return string * @since 9.1.0 */ public function getName() { return "Rename folder with user specific keys"; } /** * Run repair step. * Must throw exception on error. * * @param IOutput $output * @throws \Exception in case of failure * @since 9.1.0 */ public function run(IOutput $output) { $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); if (version_compare($versionFromBeforeUpdate, '12.0.1.5', '<=')) { $count = $this->repair(); $output->info('Repaired ' . $count . ' folders'); } } /** * rename all dirs with user specific keys to 'user-uid' * * @return int */ private function repair() { $count = 0; $dirListing = $this->appDataIdentityProof->getDirectoryListing(); /** @var ISimpleFolder $folder */ foreach ($dirListing as $folder) { $name = $folder->getName(); $node = $this->rootFolder->get($this->identityProofDir . $name); $node->move($this->identityProofDir . 'user-' . $name); $count++; } return $count; } } private/Repair/NC12/InstallCoreBundle.php 0000604 00000004214 15247130452 0014165 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC12; use OC\App\AppStore\Bundles\BundleFetcher; use OC\Installer; use OCP\IConfig; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class InstallCoreBundle implements IRepairStep { /** @var BundleFetcher */ private $bundleFetcher; /** @var IConfig */ private $config; /** @var Installer */ private $installer; /** * @param BundleFetcher $bundleFetcher * @param IConfig $config * @param Installer $installer */ public function __construct(BundleFetcher $bundleFetcher, IConfig $config, Installer $installer) { $this->bundleFetcher = $bundleFetcher; $this->config = $config; $this->installer = $installer; } /** * {@inheritdoc} */ public function getName() { return 'Install new core bundle components'; } /** * {@inheritdoc} */ public function run(IOutput $output) { $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); if (version_compare($versionFromBeforeUpdate, '12.0.0.14', '>')) { return; } $defaultBundle = $this->bundleFetcher->getDefaultInstallationBundle(); foreach($defaultBundle as $bundle) { try { $this->installer->installAppBundle($bundle); $output->info('Successfully installed core app bundle.'); } catch (\Exception $e) { $output->warning('Could not install core app bundle: ' . $e->getMessage()); } } } } private/Repair/NC12/UpdateLanguageCodes.php 0000604 00000004711 15247130452 0014462 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> * * @author Morris Jobke <hey@morrisjobke.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC12; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class UpdateLanguageCodes implements IRepairStep { /** @var IDBConnection */ private $connection; /** @var IConfig */ private $config; /** * @param IDBConnection $connection * @param IConfig $config */ public function __construct(IDBConnection $connection, IConfig $config) { $this->connection = $connection; $this->config = $config; } /** * {@inheritdoc} */ public function getName() { return 'Repair language codes'; } /** * {@inheritdoc} */ public function run(IOutput $output) { $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); if (version_compare($versionFromBeforeUpdate, '12.0.0.13', '>')) { return; } $languages = [ 'bg_BG' => 'bg', 'cs_CZ' => 'cs', 'fi_FI' => 'fi', 'hu_HU' => 'hu', 'nb_NO' => 'nb', 'sk_SK' => 'sk', 'th_TH' => 'th', ]; foreach ($languages as $oldCode => $newCode) { $qb = $this->connection->getQueryBuilder(); $affectedRows = $qb->update('preferences') ->set('configvalue', $qb->createNamedParameter($newCode)) ->where($qb->expr()->eq('appid', $qb->createNamedParameter('core'))) ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter('lang'))) ->andWhere($qb->expr()->eq('configvalue', $qb->createNamedParameter($oldCode), IQueryBuilder::PARAM_STR)) ->execute(); $output->info('Changed ' . $affectedRows . ' setting(s) from "' . $oldCode . '" to "' . $newCode . '" in preferences table.'); } } } private/Repair/RepairInvalidShares.php 0000604 00000007072 15247130452 0014055 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Repair; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; /** * Repairs shares with invalid data */ class RepairInvalidShares implements IRepairStep { const CHUNK_SIZE = 200; /** @var \OCP\IConfig */ protected $config; /** @var \OCP\IDBConnection */ protected $connection; /** * @param \OCP\IConfig $config * @param \OCP\IDBConnection $connection */ public function __construct($config, $connection) { $this->connection = $connection; $this->config = $config; } public function getName() { return 'Repair invalid shares'; } /** * Adjust file share permissions */ private function adjustFileSharePermissions(IOutput $out) { $mask = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE; $builder = $this->connection->getQueryBuilder(); $permsFunc = $builder->expr()->bitwiseAnd('permissions', $mask); $builder ->update('share') ->set('permissions', $permsFunc) ->where($builder->expr()->eq('item_type', $builder->expr()->literal('file'))) ->andWhere($builder->expr()->neq('permissions', $permsFunc)); $updatedEntries = $builder->execute(); if ($updatedEntries > 0) { $out->info('Fixed file share permissions for ' . $updatedEntries . ' shares'); } } /** * Remove shares where the parent share does not exist anymore */ private function removeSharesNonExistingParent(IOutput $out) { $deletedEntries = 0; $query = $this->connection->getQueryBuilder(); $query->select('s1.parent') ->from('share', 's1') ->where($query->expr()->isNotNull('s1.parent')) ->andWhere($query->expr()->isNull('s2.id')) ->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id')) ->groupBy('s1.parent') ->setMaxResults(self::CHUNK_SIZE); $deleteQuery = $this->connection->getQueryBuilder(); $deleteQuery->delete('share') ->where($deleteQuery->expr()->eq('parent', $deleteQuery->createParameter('parent'))); $deletedInLastChunk = self::CHUNK_SIZE; while ($deletedInLastChunk === self::CHUNK_SIZE) { $deletedInLastChunk = 0; $result = $query->execute(); while ($row = $result->fetch()) { $deletedInLastChunk++; $deletedEntries += $deleteQuery->setParameter('parent', (int) $row['parent']) ->execute(); } $result->closeCursor(); } if ($deletedEntries) { $out->info('Removed ' . $deletedEntries . ' shares where the parent did not exist'); } } public function run(IOutput $out) { $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.11', '<')) { $this->adjustFileSharePermissions($out); } $this->removeSharesNonExistingParent($out); } } private/Repair/MoveUpdaterStepFile.php 0000604 00000004313 15247130452 0014040 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> * * @author Morris Jobke <hey@morrisjobke.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class MoveUpdaterStepFile implements IRepairStep { /** @var \OCP\IConfig */ protected $config; /** * @param \OCP\IConfig $config */ public function __construct($config) { $this->config = $config; } public function getName() { return 'Move .step file of updater to backup location'; } public function run(IOutput $output) { $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data'); $instanceId = $this->config->getSystemValue('instanceid', null); if(!is_string($instanceId) || empty($instanceId)) { return; } $updaterFolderPath = $dataDir . '/updater-' . $instanceId; $stepFile = $updaterFolderPath . '/.step'; if(file_exists($stepFile)) { $output->info('.step file exists'); $previousStepFile = $updaterFolderPath . '/.step-previous-update'; // cleanup if(file_exists($previousStepFile)) { if(\OC_Helper::rmdirr($previousStepFile)) { $output->info('.step-previous-update removed'); } else { $output->info('.step-previous-update can\'t be removed - abort move of .step file'); return; } } // move step file if(rename($stepFile, $previousStepFile)) { $output->info('.step file moved to .step-previous-update'); } else { $output->warning('.step file can\'t be moved'); } } } } private/Repair/OldGroupMembershipShares.php 0000604 00000006426 15247130452 0015075 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Repair; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; use OCP\Share; class OldGroupMembershipShares implements IRepairStep { /** @var \OCP\IDBConnection */ protected $connection; /** @var \OCP\IGroupManager */ protected $groupManager; /** * @var array [gid => [uid => (bool)]] */ protected $memberships; /** * @param IDBConnection $connection * @param IGroupManager $groupManager */ public function __construct(IDBConnection $connection, IGroupManager $groupManager) { $this->connection = $connection; $this->groupManager = $groupManager; } /** * Returns the step's name * * @return string */ public function getName() { return 'Remove shares of old group memberships'; } /** * Run repair step. * Must throw exception on error. * * @throws \Exception in case of failure */ public function run(IOutput $output) { $deletedEntries = 0; $query = $this->connection->getQueryBuilder(); $query->select('s1.id')->selectAlias('s1.share_with', 'user')->selectAlias('s2.share_with', 'group') ->from('share', 's1') ->where($query->expr()->isNotNull('s1.parent')) // \OC\Share\Constant::$shareTypeGroupUserUnique === 2 ->andWhere($query->expr()->eq('s1.share_type', $query->expr()->literal(2))) ->andWhere($query->expr()->isNotNull('s2.id')) ->andWhere($query->expr()->eq('s2.share_type', $query->expr()->literal(Share::SHARE_TYPE_GROUP))) ->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id')); $deleteQuery = $this->connection->getQueryBuilder(); $deleteQuery->delete('share') ->where($query->expr()->eq('id', $deleteQuery->createParameter('share'))); $result = $query->execute(); while ($row = $result->fetch()) { if (!$this->isMember($row['group'], $row['user'])) { $deletedEntries += $deleteQuery->setParameter('share', (int) $row['id']) ->execute(); } } $result->closeCursor(); if ($deletedEntries) { $output->info('Removed ' . $deletedEntries . ' shares where user is not a member of the group anymore'); } } /** * @param string $gid * @param string $uid * @return bool */ protected function isMember($gid, $uid) { if (isset($this->memberships[$gid][$uid])) { return $this->memberships[$gid][$uid]; } $isMember = $this->groupManager->isInGroup($uid, $gid); if (!isset($this->memberships[$gid])) { $this->memberships[$gid] = []; } $this->memberships[$gid][$uid] = $isMember; return $isMember; } } private/Repair/RepairMimeTypes.php 0000604 00000007756 15247130452 0013246 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Faruk Uzun <farukuzun@collabora.com> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Normal Ra <normalraw@gmail.com> * @author Olivier Paroz <github@oparoz.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Repair; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class RepairMimeTypes implements IRepairStep { /** * @var \OCP\IConfig */ protected $config; /** * @var int */ protected $folderMimeTypeId; /** * @param \OCP\IConfig $config */ public function __construct($config) { $this->config = $config; } public function getName() { return 'Repair mime types'; } private static function existsStmt() { return \OC_DB::prepare(' SELECT count(`mimetype`) FROM `*PREFIX*mimetypes` WHERE `mimetype` = ? '); } private static function getIdStmt() { return \OC_DB::prepare(' SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ? '); } private static function insertStmt() { return \OC_DB::prepare(' INSERT INTO `*PREFIX*mimetypes` ( `mimetype` ) VALUES ( ? ) '); } private static function updateByNameStmt() { return \OC_DB::prepare(' UPDATE `*PREFIX*filecache` SET `mimetype` = ? WHERE `mimetype` <> ? AND `mimetype` <> ? AND `name` ILIKE ? '); } private function updateMimetypes($updatedMimetypes) { if (empty($this->folderMimeTypeId)) { $result = \OC_DB::executeAudited(self::getIdStmt(), array('httpd/unix-directory')); $this->folderMimeTypeId = (int)$result->fetchOne(); } foreach ($updatedMimetypes as $extension => $mimetype) { $result = \OC_DB::executeAudited(self::existsStmt(), array($mimetype)); $exists = $result->fetchOne(); if (!$exists) { // insert mimetype \OC_DB::executeAudited(self::insertStmt(), array($mimetype)); } // get target mimetype id $result = \OC_DB::executeAudited(self::getIdStmt(), array($mimetype)); $mimetypeId = $result->fetchOne(); // change mimetype for files with x extension \OC_DB::executeAudited(self::updateByNameStmt(), array($mimetypeId, $this->folderMimeTypeId, $mimetypeId, '%.' . $extension)); } } private function introduceImageTypes() { $updatedMimetypes = array( 'jp2' => 'image/jp2', 'webp' => 'image/webp', ); $this->updateMimetypes($updatedMimetypes); } private function introduceWindowsProgramTypes() { $updatedMimetypes = array( 'htaccess' => 'text/plain', 'bat' => 'application/x-msdos-program', 'cmd' => 'application/cmd', ); $this->updateMimetypes($updatedMimetypes); } /** * Fix mime types */ public function run(IOutput $out) { $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); // NOTE TO DEVELOPERS: when adding new mime types, please make sure to // add a version comparison to avoid doing it every time if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.14', '<') && $this->introduceImageTypes()) { $out->info('Fixed image mime types'); } if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.13', '<') && $this->introduceWindowsProgramTypes()) { $out->info('Fixed windows program mime types'); } } } private/Repair/CleanTags.php 0000604 00000012645 15247130452 0012021 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Repair; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUserManager; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; /** * Class RepairConfig * * @package OC\Repair */ class CleanTags implements IRepairStep { /** @var IDBConnection */ protected $connection; /** @var IUserManager */ protected $userManager; protected $deletedTags = 0; /** * @param IDBConnection $connection * @param IUserManager $userManager */ public function __construct(IDBConnection $connection, IUserManager $userManager) { $this->connection = $connection; $this->userManager = $userManager; } /** * @return string */ public function getName() { return 'Clean tags and favorites'; } /** * Updates the configuration after running an update */ public function run(IOutput $output) { $this->deleteOrphanTags($output); $this->deleteOrphanFileEntries($output); $this->deleteOrphanTagEntries($output); $this->deleteOrphanCategoryEntries($output); } /** * Delete tags for deleted users */ protected function deleteOrphanTags(IOutput $output) { $offset = 0; while ($this->checkTags($offset)) { $offset += 50; } $output->info(sprintf('%d tags of deleted users have been removed.', $this->deletedTags)); } protected function checkTags($offset) { $query = $this->connection->getQueryBuilder(); $query->select('uid') ->from('vcategory') ->groupBy('uid') ->orderBy('uid') ->setMaxResults(50) ->setFirstResult($offset); $result = $query->execute(); $users = []; $hadResults = false; while ($row = $result->fetch()) { $hadResults = true; if (!$this->userManager->userExists($row['uid'])) { $users[] = $row['uid']; } } $result->closeCursor(); if (!$hadResults) { // No more tags, stop looping return false; } if (!empty($users)) { $query = $this->connection->getQueryBuilder(); $query->delete('vcategory') ->where($query->expr()->in('uid', $query->createNamedParameter($users, IQueryBuilder::PARAM_STR_ARRAY))); $this->deletedTags += $query->execute(); } return true; } /** * Delete tag entries for deleted files */ protected function deleteOrphanFileEntries(IOutput $output) { $this->deleteOrphanEntries( $output, '%d tags for delete files have been removed.', 'vcategory_to_object', 'objid', 'filecache', 'fileid', 'path_hash' ); } /** * Delete tag entries for deleted tags */ protected function deleteOrphanTagEntries(IOutput $output) { $this->deleteOrphanEntries( $output, '%d tag entries for deleted tags have been removed.', 'vcategory_to_object', 'categoryid', 'vcategory', 'id', 'uid' ); } /** * Delete tags that have no entries */ protected function deleteOrphanCategoryEntries(IOutput $output) { $this->deleteOrphanEntries( $output, '%d tags with no entries have been removed.', 'vcategory', 'id', 'vcategory_to_object', 'categoryid', 'type' ); } /** * Deletes all entries from $deleteTable that do not have a matching entry in $sourceTable * * A query joins $deleteTable.$deleteId = $sourceTable.$sourceId and checks * whether $sourceNullColumn is null. If it is null, the entry in $deleteTable * is being deleted. * * @param string $repairInfo * @param string $deleteTable * @param string $deleteId * @param string $sourceTable * @param string $sourceId * @param string $sourceNullColumn If this column is null in the source table, * the entry is deleted in the $deleteTable */ protected function deleteOrphanEntries(IOutput $output, $repairInfo, $deleteTable, $deleteId, $sourceTable, $sourceId, $sourceNullColumn) { $qb = $this->connection->getQueryBuilder(); $qb->select('d.' . $deleteId) ->from($deleteTable, 'd') ->leftJoin('d', $sourceTable, 's', $qb->expr()->eq('d.' . $deleteId, 's.' . $sourceId)) ->where( $qb->expr()->eq('d.type', $qb->expr()->literal('files')) ) ->andWhere( $qb->expr()->isNull('s.' . $sourceNullColumn) ); $result = $qb->execute(); $orphanItems = array(); while ($row = $result->fetch()) { $orphanItems[] = (int) $row[$deleteId]; } if (!empty($orphanItems)) { $orphanItemsBatch = array_chunk($orphanItems, 200); foreach ($orphanItemsBatch as $items) { $qb->delete($deleteTable) ->where( $qb->expr()->eq('type', $qb->expr()->literal('files')) ) ->andWhere($qb->expr()->in($deleteId, $qb->createParameter('ids'))); $qb->setParameter('ids', $items, IQueryBuilder::PARAM_INT_ARRAY); $qb->execute(); } } if ($repairInfo) { $output->info(sprintf($repairInfo, sizeof($orphanItems))); } } } private/Repair/NC13/RepairInvalidPaths.php 0000604 00000013045 15247130452 0014350 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\NC13; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class RepairInvalidPaths implements IRepairStep { const MAX_ROWS = 1000; /** @var IDBConnection */ private $connection; /** @var IConfig */ private $config; private $getIdQuery; private $updateQuery; private $reparentQuery; private $deleteQuery; public function __construct(IDBConnection $connection, IConfig $config) { $this->connection = $connection; $this->config = $config; } public function getName() { return 'Repair invalid paths in file cache'; } private function getInvalidEntries() { $builder = $this->connection->getQueryBuilder(); $computedPath = $builder->func()->concat( 'p.path', $builder->func()->concat($builder->createNamedParameter('/'), 'f.name') ); //select f.path, f.parent,p.path from oc_filecache f inner join oc_filecache p on f.parent=p.fileid and p.path!='' where f.path != p.path || '/' || f.name; $builder->select('f.fileid', 'f.path', 'f.name', 'f.parent', 'f.storage') ->selectAlias('p.path', 'parent_path') ->selectAlias('p.storage', 'parent_storage') ->from('filecache', 'f') ->innerJoin('f', 'filecache', 'p', $builder->expr()->andX( $builder->expr()->eq('f.parent', 'p.fileid'), $builder->expr()->nonEmptyString('p.name') )) ->where($builder->expr()->neq('f.path', $computedPath)) ->setMaxResults(self::MAX_ROWS); do { $result = $builder->execute(); $rows = $result->fetchAll(); foreach ($rows as $row) { yield $row; } $result->closeCursor(); } while (count($rows) > 0); } private function getId($storage, $path) { if (!$this->getIdQuery) { $builder = $this->connection->getQueryBuilder(); $this->getIdQuery = $builder->select('fileid') ->from('filecache') ->where($builder->expr()->eq('storage', $builder->createParameter('storage'))) ->andWhere($builder->expr()->eq('path_hash', $builder->createParameter('path_hash'))); } $this->getIdQuery->setParameter('storage', $storage, IQueryBuilder::PARAM_INT); $this->getIdQuery->setParameter('path_hash', md5($path)); return $this->getIdQuery->execute()->fetchColumn(); } private function update($fileid, $newPath, $newStorage) { if (!$this->updateQuery) { $builder = $this->connection->getQueryBuilder(); $this->updateQuery = $builder->update('filecache') ->set('path', $builder->createParameter('newpath')) ->set('path_hash', $builder->func()->md5($builder->createParameter('newpath'))) ->set('storage', $builder->createParameter('newstorage')) ->where($builder->expr()->eq('fileid', $builder->createParameter('fileid'))); } $this->updateQuery->setParameter('newpath', $newPath); $this->updateQuery->setParameter('newstorage', $newStorage); $this->updateQuery->setParameter('fileid', $fileid, IQueryBuilder::PARAM_INT); $this->updateQuery->execute(); } private function reparent($from, $to) { if (!$this->reparentQuery) { $builder = $this->connection->getQueryBuilder(); $this->reparentQuery = $builder->update('filecache') ->set('parent', $builder->createParameter('to')) ->where($builder->expr()->eq('fileid', $builder->createParameter('from'))); } $this->reparentQuery->setParameter('from', $from); $this->reparentQuery->setParameter('to', $to); $this->reparentQuery->execute(); } private function delete($fileid) { if (!$this->deleteQuery) { $builder = $this->connection->getQueryBuilder(); $this->deleteQuery = $builder->delete('filecache') ->where($builder->expr()->eq('fileid', $builder->createParameter('fileid'))); } $this->deleteQuery->setParameter('fileid', $fileid, IQueryBuilder::PARAM_INT); $this->deleteQuery->execute(); } private function repair() { $this->connection->beginTransaction(); $entries = $this->getInvalidEntries(); $count = 0; foreach ($entries as $entry) { $count++; $calculatedPath = $entry['parent_path'] . '/' . $entry['name']; if ($newId = $this->getId($entry['parent_storage'], $calculatedPath)) { // a new entry with the correct path has already been created, reuse that one and delete the incorrect entry $this->reparent($entry['fileid'], $newId); $this->delete($entry['fileid']); } else { $this->update($entry['fileid'], $calculatedPath, $entry['parent_storage']); } } $this->connection->commit(); return $count; } public function run(IOutput $output) { $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); // was added to 12.0.0.30 and 13.0.0.1 if (version_compare($versionFromBeforeUpdate, '12.0.0.30', '<') || version_compare($versionFromBeforeUpdate, '13.0.0.0', '==')) { $count = $this->repair(); $output->info('Repaired ' . $count . ' paths'); } } } private/Repair/RemoveRootShares.php 0000604 00000006640 15247130452 0013425 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Repair; use OCP\Files\IRootFolder; use OCP\IDBConnection; use OCP\IUser; use OCP\IUserManager; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; /** * Class RemoveRootShares * * @package OC\Repair */ class RemoveRootShares implements IRepairStep { /** @var IDBConnection */ protected $connection; /** @var IUserManager */ protected $userManager; /** @var IRootFolder */ protected $rootFolder; /** * RemoveRootShares constructor. * * @param IDBConnection $connection * @param IUserManager $userManager * @param IRootFolder $rootFolder */ public function __construct(IDBConnection $connection, IUserManager $userManager, IRootFolder $rootFolder) { $this->connection = $connection; $this->userManager = $userManager; $this->rootFolder = $rootFolder; } /** * @return string */ public function getName() { return 'Remove shares of a users root folder'; } /** * @param IOutput $output */ public function run(IOutput $output) { if ($this->rootSharesExist()) { $this->removeRootShares($output); } } /** * @param IOutput $output */ private function removeRootShares(IOutput $output) { $function = function(IUser $user) use ($output) { $userFolder = $this->rootFolder->getUserFolder($user->getUID()); $fileId = $userFolder->getId(); $qb = $this->connection->getQueryBuilder(); $qb->delete('share') ->where($qb->expr()->eq('file_source', $qb->createNamedParameter($fileId))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->expr()->literal('file')), $qb->expr()->eq('item_type', $qb->expr()->literal('folder')) )); $qb->execute(); $output->advance(); }; $output->startProgress($this->userManager->countSeenUsers()); $this->userManager->callForSeenUsers($function); $output->finishProgress(); } /** * Verify if this repair steps is required * It *should* not be necessary in most cases and it can be very * costly. * * @return bool */ private function rootSharesExist() { $qb = $this->connection->getQueryBuilder(); $qb2 = $this->connection->getQueryBuilder(); $qb->select('fileid') ->from('filecache') ->where($qb->expr()->eq('path', $qb->expr()->literal('files'))); $qb2->select('id') ->from('share') ->where($qb2->expr()->in('file_source', $qb2->createFunction($qb->getSQL()))) ->andWhere($qb2->expr()->orX( $qb2->expr()->eq('item_type', $qb->expr()->literal('file')), $qb2->expr()->eq('item_type', $qb->expr()->literal('folder')) )) ->setMaxResults(1); $cursor = $qb2->execute(); $data = $cursor->fetch(); $cursor->closeCursor(); if ($data === false) { return false; } return true; } } private/Repair/Owncloud/SaveAccountsTableData.php 0000604 00000011254 15247130452 0016105 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\Owncloud; use OC\DB\Connection; use OC\DB\MDB2SchemaManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; use OCP\PreConditionNotMetException; /** * Copies the email address from the accounts table to the preference table, * before the data structure is changed and the information is gone */ class SaveAccountsTableData implements IRepairStep { const BATCH_SIZE = 75; /** @var IDBConnection|Connection */ protected $db; /** @var IConfig */ protected $config; /** * @param IDBConnection $db * @param IConfig $config */ public function __construct(IDBConnection $db, IConfig $config) { $this->db = $db; $this->config = $config; } /** * @return string */ public function getName() { return 'Copy data from accounts table when migrating from ownCloud'; } /** * @param IOutput $output */ public function run(IOutput $output) { if (!$this->shouldRun()) { return; } $offset = 0; $numUsers = $this->runStep($offset); while ($numUsers === self::BATCH_SIZE) { $offset += $numUsers; $numUsers = $this->runStep($offset); } // Remove the table $this->db->dropTable('accounts'); } /** * @return bool */ protected function shouldRun() { // This is the equivalent of the new migration code that is used in 13+ $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; $this->db->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); $schema = $this->db->getSchemaManager()->createSchema(); $tableName = $this->config->getSystemValue('dbtableprefix', 'oc_') . 'accounts'; if (!$schema->hasTable($tableName)) { return false; } $table = $schema->getTable($tableName); return $table->hasColumn('user_id'); } /** * @param int $offset * @return int Number of copied users */ protected function runStep($offset) { $query = $this->db->getQueryBuilder(); $query->select('*') ->from('accounts') ->orderBy('id') ->setMaxResults(self::BATCH_SIZE); if ($offset > 0) { $query->setFirstResult($offset); } $result = $query->execute(); $update = $this->db->getQueryBuilder(); $update->update('users') ->set('displayname', $update->createParameter('displayname')) ->where($update->expr()->eq('uid', $update->createParameter('userid'))); $updatedUsers = 0; while ($row = $result->fetch()) { try { $this->migrateUserInfo($update, $row); } catch (PreConditionNotMetException $e) { // Ignore and continue } catch (\UnexpectedValueException $e) { // Ignore and continue } $updatedUsers++; } $result->closeCursor(); return $updatedUsers; } /** * @param IQueryBuilder $update * @param array $userdata * @throws PreConditionNotMetException * @throws \UnexpectedValueException */ protected function migrateUserInfo(IQueryBuilder $update, $userdata) { $state = (int) $userdata['state']; if ($state === 3) { // Deleted user, ignore return; } if ($userdata['email'] !== null) { $this->config->setUserValue($userdata['user_id'], 'settings', 'email', $userdata['email']); } if ($userdata['quota'] !== null) { $this->config->setUserValue($userdata['user_id'], 'files', 'quota', $userdata['quota']); } if ($userdata['last_login'] !== null) { $this->config->setUserValue($userdata['user_id'], 'login', 'lastLogin', $userdata['last_login']); } if ($state === 1) { $this->config->setUserValue($userdata['user_id'], 'core', 'enabled', 'true'); } else if ($state === 2) { $this->config->setUserValue($userdata['user_id'], 'core', 'enabled', 'false'); } if ($userdata['display_name'] !== null) { $update->setParameter('displayname', $userdata['display_name']) ->setParameter('userid', $userdata['user_id']); $update->execute(); } } } private/Repair/Owncloud/DropAccountTermsTable.php 0000604 00000002741 15247130452 0016152 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Repair\Owncloud; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class DropAccountTermsTable implements IRepairStep { /** @var IDBConnection */ protected $db; /** * @param IDBConnection $db */ public function __construct(IDBConnection $db) { $this->db = $db; } /** * @return string */ public function getName() { return 'Drop account terms table when migrating from ownCloud'; } /** * @param IOutput $output */ public function run(IOutput $output) { if (!$this->db->tableExists('account_terms')) { return; } $this->db->dropTable('account_terms'); } } private/legacy/image.php 0000604 00000104023 15247130452 0011254 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bartek Przybylski <bart.p.pl@gmail.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Byron Marohn <combustible@live.com> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Georg Ehrke <georg@owncloud.com> * @author j-ed <juergen@eisfair.org> * @author Joas Schilling <coding@schilljs.com> * @author Johannes Willnecker <johannes@willnecker.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Class for basic image manipulation */ class OC_Image implements \OCP\IImage { /** @var false|resource */ protected $resource = false; // tmp resource. /** @var int */ protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident. /** @var string */ protected $mimeType = 'image/png'; // Default to png /** @var int */ protected $bitDepth = 24; /** @var null|string */ protected $filePath = null; /** @var finfo */ private $fileInfo; /** @var \OCP\ILogger */ private $logger; /** @var \OCP\IConfig */ private $config; /** @var array */ private $exif; /** * Get mime type for an image file. * * @param string|null $filePath The path to a local image file. * @return string The mime type if the it could be determined, otherwise an empty string. */ static public function getMimeTypeForFile($filePath) { // exif_imagetype throws "read error!" if file is less than 12 byte if ($filePath !== null && filesize($filePath) > 11) { $imageType = exif_imagetype($filePath); } else { $imageType = false; } return $imageType ? image_type_to_mime_type($imageType) : ''; } /** * Constructor. * * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by * an imagecreate* function. * @param \OCP\ILogger $logger * @param \OCP\IConfig $config */ public function __construct($imageRef = null, \OCP\ILogger $logger = null, \OCP\IConfig $config = null) { $this->logger = $logger; if ($logger === null) { $this->logger = \OC::$server->getLogger(); } $this->config = $config; if ($config === null) { $this->config = \OC::$server->getConfig(); } if (\OC_Util::fileInfoLoaded()) { $this->fileInfo = new finfo(FILEINFO_MIME_TYPE); } if ($imageRef !== null) { $this->load($imageRef); } } /** * Determine whether the object contains an image resource. * * @return bool */ public function valid() { // apparently you can't name a method 'empty'... return is_resource($this->resource); } /** * Returns the MIME type of the image or an empty string if no image is loaded. * * @return string */ public function mimeType() { return $this->valid() ? $this->mimeType : ''; } /** * Returns the width of the image or -1 if no image is loaded. * * @return int */ public function width() { return $this->valid() ? imagesx($this->resource) : -1; } /** * Returns the height of the image or -1 if no image is loaded. * * @return int */ public function height() { return $this->valid() ? imagesy($this->resource) : -1; } /** * Returns the width when the image orientation is top-left. * * @return int */ public function widthTopLeft() { $o = $this->getOrientation(); $this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, array('app' => 'core')); switch ($o) { case -1: case 1: case 2: // Not tested case 3: case 4: // Not tested return $this->width(); case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->height(); } return $this->width(); } /** * Returns the height when the image orientation is top-left. * * @return int */ public function heightTopLeft() { $o = $this->getOrientation(); $this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, array('app' => 'core')); switch ($o) { case -1: case 1: case 2: // Not tested case 3: case 4: // Not tested return $this->height(); case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->width(); } return $this->height(); } /** * Outputs the image. * * @param string $mimeType * @return bool */ public function show($mimeType = null) { if ($mimeType === null) { $mimeType = $this->mimeType(); } header('Content-Type: ' . $mimeType); return $this->_output(null, $mimeType); } /** * Saves the image. * * @param string $filePath * @param string $mimeType * @return bool */ public function save($filePath = null, $mimeType = null) { if ($mimeType === null) { $mimeType = $this->mimeType(); } if ($filePath === null) { if ($this->filePath === null) { $this->logger->error(__METHOD__ . '(): called with no path.', array('app' => 'core')); return false; } else { $filePath = $this->filePath; } } return $this->_output($filePath, $mimeType); } /** * Outputs/saves the image. * * @param string $filePath * @param string $mimeType * @return bool * @throws Exception */ private function _output($filePath = null, $mimeType = null) { if ($filePath) { if (!file_exists(dirname($filePath))) { mkdir(dirname($filePath), 0777, true); } $isWritable = is_writable(dirname($filePath)); if (!$isWritable) { $this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', array('app' => 'core')); return false; } elseif ($isWritable && file_exists($filePath) && !is_writable($filePath)) { $this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', array('app' => 'core')); return false; } } if (!$this->valid()) { return false; } $imageType = $this->imageType; if ($mimeType !== null) { switch ($mimeType) { case 'image/gif': $imageType = IMAGETYPE_GIF; break; case 'image/jpeg': $imageType = IMAGETYPE_JPEG; break; case 'image/png': $imageType = IMAGETYPE_PNG; break; case 'image/x-xbitmap': $imageType = IMAGETYPE_XBM; break; case 'image/bmp': case 'image/x-ms-bmp': $imageType = IMAGETYPE_BMP; break; default: throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format'); } } switch ($imageType) { case IMAGETYPE_GIF: $retVal = imagegif($this->resource, $filePath); break; case IMAGETYPE_JPEG: $retVal = imagejpeg($this->resource, $filePath, $this->getJpegQuality()); break; case IMAGETYPE_PNG: $retVal = imagepng($this->resource, $filePath); break; case IMAGETYPE_XBM: if (function_exists('imagexbm')) { $retVal = imagexbm($this->resource, $filePath); } else { throw new Exception('\OC_Image::_output(): imagexbm() is not supported.'); } break; case IMAGETYPE_WBMP: $retVal = imagewbmp($this->resource, $filePath); break; case IMAGETYPE_BMP: $retVal = imagebmp($this->resource, $filePath, $this->bitDepth); break; default: $retVal = imagepng($this->resource, $filePath); } return $retVal; } /** * Prints the image when called as $image(). */ public function __invoke() { return $this->show(); } /** * @return resource Returns the image resource in any. */ public function resource() { return $this->resource; } /** * @return null|string Returns the raw image data. */ public function data() { if (!$this->valid()) { return null; } ob_start(); switch ($this->mimeType) { case "image/png": $res = imagepng($this->resource); break; case "image/jpeg": $quality = $this->getJpegQuality(); if ($quality !== null) { $res = imagejpeg($this->resource, null, $quality); } else { $res = imagejpeg($this->resource); } break; case "image/gif": $res = imagegif($this->resource); break; default: $res = imagepng($this->resource); $this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', array('app' => 'core')); break; } if (!$res) { $this->logger->error('OC_Image->data. Error getting image data.', array('app' => 'core')); } return ob_get_clean(); } /** * @return string - base64 encoded, which is suitable for embedding in a VCard. */ public function __toString() { return base64_encode($this->data()); } /** * @return int|null */ protected function getJpegQuality() { $quality = $this->config->getAppValue('preview', 'jpeg_quality', 90); if ($quality !== null) { $quality = min(100, max(10, (int) $quality)); } return $quality; } /** * (I'm open for suggestions on better method name ;) * Get the orientation based on EXIF data. * * @return int The orientation or -1 if no EXIF data is available. */ public function getOrientation() { if ($this->exif !== null) { return $this->exif['Orientation']; } if ($this->imageType !== IMAGETYPE_JPEG) { $this->logger->debug('OC_Image->fixOrientation() Image is not a JPEG.', array('app' => 'core')); return -1; } if (!is_callable('exif_read_data')) { $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core')); return -1; } if (!$this->valid()) { $this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core')); return -1; } if (is_null($this->filePath) || !is_readable($this->filePath)) { $this->logger->debug('OC_Image->fixOrientation() No readable file path set.', array('app' => 'core')); return -1; } $exif = @exif_read_data($this->filePath, 'IFD0'); if (!$exif) { return -1; } if (!isset($exif['Orientation'])) { return -1; } $this->exif = $exif; return $exif['Orientation']; } public function readExif($data) { if (!is_callable('exif_read_data')) { $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core')); return; } if (!$this->valid()) { $this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core')); return; } $exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data)); if (!$exif) { return; } if (!isset($exif['Orientation'])) { return; } $this->exif = $exif; } /** * (I'm open for suggestions on better method name ;) * Fixes orientation based on EXIF data. * * @return bool. */ public function fixOrientation() { $o = $this->getOrientation(); $this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, array('app' => 'core')); $rotate = 0; $flip = false; switch ($o) { case -1: return false; //Nothing to fix case 1: $rotate = 0; break; case 2: $rotate = 0; $flip = true; break; case 3: $rotate = 180; break; case 4: $rotate = 180; $flip = true; break; case 5: $rotate = 90; $flip = true; break; case 6: $rotate = 270; break; case 7: $rotate = 270; $flip = true; break; case 8: $rotate = 90; break; } if($flip && function_exists('imageflip')) { imageflip($this->resource, IMG_FLIP_HORIZONTAL); } if ($rotate) { $res = imagerotate($this->resource, $rotate, 0); if ($res) { if (imagealphablending($res, true)) { if (imagesavealpha($res, true)) { imagedestroy($this->resource); $this->resource = $res; return true; } else { $this->logger->debug('OC_Image->fixOrientation() Error during alpha-saving', array('app' => 'core')); return false; } } else { $this->logger->debug('OC_Image->fixOrientation() Error during alpha-blending', array('app' => 'core')); return false; } } else { $this->logger->debug('OC_Image->fixOrientation() Error during orientation fixing', array('app' => 'core')); return false; } } return false; } /** * Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function. * * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by an imagecreate* function or a file resource (file handle ). * @return resource|false An image resource or false on error */ public function load($imageRef) { if (is_resource($imageRef)) { if (get_resource_type($imageRef) === 'gd') { $this->resource = $imageRef; return $this->resource; } elseif (in_array(get_resource_type($imageRef), array('file', 'stream'))) { return $this->loadFromFileHandle($imageRef); } } elseif ($this->loadFromBase64($imageRef) !== false) { return $this->resource; } elseif ($this->loadFromFile($imageRef) !== false) { return $this->resource; } elseif ($this->loadFromData($imageRef) !== false) { return $this->resource; } $this->logger->debug(__METHOD__ . '(): could not load anything. Giving up!', array('app' => 'core')); return false; } /** * Loads an image from an open file handle. * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again. * * @param resource $handle * @return resource|false An image resource or false on error */ public function loadFromFileHandle($handle) { $contents = stream_get_contents($handle); if ($this->loadFromData($contents)) { return $this->resource; } return false; } /** * Loads an image from a local file. * * @param bool|string $imagePath The path to a local file. * @return bool|resource An image resource or false on error */ public function loadFromFile($imagePath = false) { // exif_imagetype throws "read error!" if file is less than 12 byte if (!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) { return false; } $iType = exif_imagetype($imagePath); switch ($iType) { case IMAGETYPE_GIF: if (imagetypes() & IMG_GIF) { $this->resource = imagecreatefromgif($imagePath); // Preserve transparency imagealphablending($this->resource, true); imagesavealpha($this->resource, true); } else { $this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_JPEG: if (imagetypes() & IMG_JPG) { if (getimagesize($imagePath) !== false) { $this->resource = @imagecreatefromjpeg($imagePath); } else { $this->logger->debug('OC_Image->loadFromFile, JPG image not valid: ' . $imagePath, array('app' => 'core')); } } else { $this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_PNG: if (imagetypes() & IMG_PNG) { $this->resource = @imagecreatefrompng($imagePath); // Preserve transparency imagealphablending($this->resource, true); imagesavealpha($this->resource, true); } else { $this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_XBM: if (imagetypes() & IMG_XPM) { $this->resource = @imagecreatefromxbm($imagePath); } else { $this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_WBMP: if (imagetypes() & IMG_WBMP) { $this->resource = @imagecreatefromwbmp($imagePath); } else { $this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, array('app' => 'core')); } break; case IMAGETYPE_BMP: $this->resource = $this->imagecreatefrombmp($imagePath); break; /* case IMAGETYPE_TIFF_II: // (intel byte order) break; case IMAGETYPE_TIFF_MM: // (motorola byte order) break; case IMAGETYPE_JPC: break; case IMAGETYPE_JP2: break; case IMAGETYPE_JPX: break; case IMAGETYPE_JB2: break; case IMAGETYPE_SWC: break; case IMAGETYPE_IFF: break; case IMAGETYPE_ICO: break; case IMAGETYPE_SWF: break; case IMAGETYPE_PSD: break; */ default: // this is mostly file created from encrypted file $this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagePath))); $iType = IMAGETYPE_PNG; $this->logger->debug('OC_Image->loadFromFile, Default', array('app' => 'core')); break; } if ($this->valid()) { $this->imageType = $iType; $this->mimeType = image_type_to_mime_type($iType); $this->filePath = $imagePath; } return $this->resource; } /** * Loads an image from a string of data. * * @param string $str A string of image data as read from a file. * @return bool|resource An image resource or false on error */ public function loadFromData($str) { if (is_resource($str)) { return false; } $this->resource = @imagecreatefromstring($str); if ($this->fileInfo) { $this->mimeType = $this->fileInfo->buffer($str); } if (is_resource($this->resource)) { imagealphablending($this->resource, false); imagesavealpha($this->resource, true); } if (!$this->resource) { $this->logger->debug('OC_Image->loadFromFile, could not load', array('app' => 'core')); return false; } return $this->resource; } /** * Loads an image from a base64 encoded string. * * @param string $str A string base64 encoded string of image data. * @return bool|resource An image resource or false on error */ public function loadFromBase64($str) { if (!is_string($str)) { return false; } $data = base64_decode($str); if ($data) { // try to load from string data $this->resource = @imagecreatefromstring($data); if ($this->fileInfo) { $this->mimeType = $this->fileInfo->buffer($data); } if (!$this->resource) { $this->logger->debug('OC_Image->loadFromBase64, could not load', array('app' => 'core')); return false; } return $this->resource; } else { return false; } } /** * Create a new image from file or URL * * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm * @version 1.00 * @param string $fileName <p> * Path to the BMP image. * </p> * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors. */ private function imagecreatefrombmp($fileName) { if (!($fh = fopen($fileName, 'rb'))) { $this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName, array('app' => 'core')); return false; } // read file header $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14)); // check for bitmap if ($meta['type'] != 19778) { fclose($fh); $this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName . ' is not a bitmap!', array('app' => 'core')); return false; } // read image header $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40)); // read additional 16bit header if ($meta['bits'] == 16) { $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12)); } // set bytes and padding $meta['bytes'] = $meta['bits'] / 8; $this->bitDepth = $meta['bits']; //remember the bit depth for the imagebmp call $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4))); if ($meta['decal'] == 4) { $meta['decal'] = 0; } // obtain imagesize if ($meta['imagesize'] < 1) { $meta['imagesize'] = $meta['filesize'] - $meta['offset']; // in rare cases filesize is equal to offset so we need to read physical size if ($meta['imagesize'] < 1) { $meta['imagesize'] = @filesize($fileName) - $meta['offset']; if ($meta['imagesize'] < 1) { fclose($fh); $this->logger->warning('imagecreatefrombmp: Can not obtain file size of ' . $fileName . ' is not a bitmap!', array('app' => 'core')); return false; } } } // calculate colors $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; // read color palette $palette = array(); if ($meta['bits'] < 16) { $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); // in rare cases the color value is signed if ($palette[1] < 0) { foreach ($palette as $i => $color) { $palette[$i] = $color + 16777216; } } } // create gd image $im = imagecreatetruecolor($meta['width'], $meta['height']); if ($im == false) { fclose($fh); $this->logger->warning( 'imagecreatefrombmp: imagecreatetruecolor failed for file "' . $fileName . '" with dimensions ' . $meta['width'] . 'x' . $meta['height'], array('app' => 'core')); return false; } $data = fread($fh, $meta['imagesize']); $p = 0; $vide = chr(0); $y = $meta['height'] - 1; $error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!'; // loop through the image data beginning with the lower left corner while ($y >= 0) { $x = 0; while ($x < $meta['width']) { switch ($meta['bits']) { case 32: case 24: if (!($part = substr($data, $p, 3))) { $this->logger->warning($error, array('app' => 'core')); return $im; } $color = @unpack('V', $part . $vide); break; case 16: if (!($part = substr($data, $p, 2))) { fclose($fh); $this->logger->warning($error, array('app' => 'core')); return $im; } $color = @unpack('v', $part); $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); break; case 8: $color = @unpack('n', $vide . substr($data, $p, 1)); $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1]; break; case 4: $color = @unpack('n', $vide . substr($data, floor($p), 1)); $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F; $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1]; break; case 1: $color = @unpack('n', $vide . substr($data, floor($p), 1)); switch (($p * 8) % 8) { case 0: $color[1] = $color[1] >> 7; break; case 1: $color[1] = ($color[1] & 0x40) >> 6; break; case 2: $color[1] = ($color[1] & 0x20) >> 5; break; case 3: $color[1] = ($color[1] & 0x10) >> 4; break; case 4: $color[1] = ($color[1] & 0x8) >> 3; break; case 5: $color[1] = ($color[1] & 0x4) >> 2; break; case 6: $color[1] = ($color[1] & 0x2) >> 1; break; case 7: $color[1] = ($color[1] & 0x1); break; } $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1]; break; default: fclose($fh); $this->logger->warning('imagecreatefrombmp: ' . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!', array('app' => 'core')); return false; } imagesetpixel($im, $x, $y, $color[1]); $x++; $p += $meta['bytes']; } $y--; $p += $meta['decal']; } fclose($fh); return $im; } /** * Resizes the image preserving ratio. * * @param integer $maxSize The maximum size of either the width or height. * @return bool */ public function resize($maxSize) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); $ratioOrig = $widthOrig / $heightOrig; if ($ratioOrig > 1) { $newHeight = round($maxSize / $ratioOrig); $newWidth = $maxSize; } else { $newWidth = round($maxSize * $ratioOrig); $newHeight = $maxSize; } $this->preciseResize(round($newWidth), round($newHeight)); return true; } /** * @param int $width * @param int $height * @return bool */ public function preciseResize($width, $height) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); $process = imagecreatetruecolor($width, $height); if ($process == false) { $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core')); imagedestroy($process); return false; } // preserve transparency if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); imagealphablending($process, false); imagesavealpha($process, true); } imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig); if ($process == false) { $this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core')); imagedestroy($process); return false; } imagedestroy($this->resource); $this->resource = $process; return true; } /** * Crops the image to the middle square. If the image is already square it just returns. * * @param int $size maximum size for the result (optional) * @return bool for success or failure */ public function centerCrop($size = 0) { if (!$this->valid()) { $this->logger->error('OC_Image->centerCrop, No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); if ($widthOrig === $heightOrig and $size == 0) { return true; } $ratioOrig = $widthOrig / $heightOrig; $width = $height = min($widthOrig, $heightOrig); if ($ratioOrig > 1) { $x = ($widthOrig / 2) - ($width / 2); $y = 0; } else { $y = ($heightOrig / 2) - ($height / 2); $x = 0; } if ($size > 0) { $targetWidth = $size; $targetHeight = $size; } else { $targetWidth = $width; $targetHeight = $height; } $process = imagecreatetruecolor($targetWidth, $targetHeight); if ($process == false) { $this->logger->error('OC_Image->centerCrop, Error creating true color image', array('app' => 'core')); imagedestroy($process); return false; } // preserve transparency if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); imagealphablending($process, false); imagesavealpha($process, true); } imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height); if ($process == false) { $this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core')); imagedestroy($process); return false; } imagedestroy($this->resource); $this->resource = $process; return true; } /** * Crops the image from point $x$y with dimension $wx$h. * * @param int $x Horizontal position * @param int $y Vertical position * @param int $w Width * @param int $h Height * @return bool for success or failure */ public function crop($x, $y, $w, $h) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $process = imagecreatetruecolor($w, $h); if ($process == false) { $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core')); imagedestroy($process); return false; } // preserve transparency if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); imagealphablending($process, false); imagesavealpha($process, true); } imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h); if ($process == false) { $this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, array('app' => 'core')); imagedestroy($process); return false; } imagedestroy($this->resource); $this->resource = $process; return true; } /** * Resizes the image to fit within a boundary while preserving ratio. * * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up * * @param integer $maxWidth * @param integer $maxHeight * @return bool */ public function fitIn($maxWidth, $maxHeight) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); $ratio = $widthOrig / $heightOrig; $newWidth = min($maxWidth, $ratio * $maxHeight); $newHeight = min($maxHeight, $maxWidth / $ratio); $this->preciseResize(round($newWidth), round($newHeight)); return true; } /** * Shrinks larger images to fit within specified boundaries while preserving ratio. * * @param integer $maxWidth * @param integer $maxHeight * @return bool */ public function scaleDownToFit($maxWidth, $maxHeight) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core')); return false; } $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); if ($widthOrig > $maxWidth || $heightOrig > $maxHeight) { return $this->fitIn($maxWidth, $maxHeight); } return false; } /** * Destroys the current image and resets the object */ public function destroy() { if ($this->valid()) { imagedestroy($this->resource); } $this->resource = null; } public function __destruct() { $this->destroy(); } } if (!function_exists('imagebmp')) { /** * Output a BMP image to either the browser or a file * * @link http://www.ugia.cn/wp-data/imagebmp.php * @author legend <legendsky@hotmail.com> * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm * @author mgutt <marc@gutt.it> * @version 1.00 * @param resource $im * @param string $fileName [optional] <p>The path to save the file to.</p> * @param int $bit [optional] <p>Bit depth, (default is 24).</p> * @param int $compression [optional] * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure. */ function imagebmp($im, $fileName = '', $bit = 24, $compression = 0) { if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) { $bit = 24; } else if ($bit == 32) { $bit = 24; } $bits = pow(2, $bit); imagetruecolortopalette($im, true, $bits); $width = imagesx($im); $height = imagesy($im); $colorsNum = imagecolorstotal($im); $rgbQuad = ''; if ($bit <= 8) { for ($i = 0; $i < $colorsNum; $i++) { $colors = imagecolorsforindex($im, $i); $rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0"; } $bmpData = ''; if ($compression == 0 || $bit < 8) { $compression = 0; $extra = ''; $padding = 4 - ceil($width / (8 / $bit)) % 4; if ($padding % 4 != 0) { $extra = str_repeat("\0", $padding); } for ($j = $height - 1; $j >= 0; $j--) { $i = 0; while ($i < $width) { $bin = 0; $limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0; for ($k = 8 - $bit; $k >= $limit; $k -= $bit) { $index = imagecolorat($im, $i, $j); $bin |= $index << $k; $i++; } $bmpData .= chr($bin); } $bmpData .= $extra; } } // RLE8 else if ($compression == 1 && $bit == 8) { for ($j = $height - 1; $j >= 0; $j--) { $lastIndex = "\0"; $sameNum = 0; for ($i = 0; $i <= $width; $i++) { $index = imagecolorat($im, $i, $j); if ($index !== $lastIndex || $sameNum > 255) { if ($sameNum != 0) { $bmpData .= chr($sameNum) . chr($lastIndex); } $lastIndex = $index; $sameNum = 1; } else { $sameNum++; } } $bmpData .= "\0\0"; } $bmpData .= "\0\1"; } $sizeQuad = strlen($rgbQuad); $sizeData = strlen($bmpData); } else { $extra = ''; $padding = 4 - ($width * ($bit / 8)) % 4; if ($padding % 4 != 0) { $extra = str_repeat("\0", $padding); } $bmpData = ''; for ($j = $height - 1; $j >= 0; $j--) { for ($i = 0; $i < $width; $i++) { $index = imagecolorat($im, $i, $j); $colors = imagecolorsforindex($im, $index); if ($bit == 16) { $bin = 0 << $bit; $bin |= ($colors['red'] >> 3) << 10; $bin |= ($colors['green'] >> 3) << 5; $bin |= $colors['blue'] >> 3; $bmpData .= pack("v", $bin); } else { $bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']); } } $bmpData .= $extra; } $sizeQuad = 0; $sizeData = strlen($bmpData); $colorsNum = 0; } $fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad); $infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0); if ($fileName != '') { $fp = fopen($fileName, 'wb'); fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData); fclose($fp); return true; } echo $fileHeader . $infoHeader . $rgbQuad . $bmpData; return true; } } if (!function_exists('exif_imagetype')) { /** * Workaround if exif_imagetype does not exist * * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383 * @param string $fileName * @return string|boolean */ function exif_imagetype($fileName) { if (($info = getimagesize($fileName)) !== false) { return $info[2]; } return false; } } private/legacy/template.php 0000604 00000031511 15247130452 0012006 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Brice Maron <brice@bmaron.net> * @author Frank Karlitschek <frank@karlitschek.de> * @author Hendrik Leppelsack <hendrik@leppelsack.de> * @author Individual IT Services <info@individual-it.net> * @author Jakob Sack <mail@jakobsack.de> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Raghu Nayyar <hey@raghunayyar.com> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OC\TemplateLayout; require_once __DIR__.'/template/functions.php'; /** * This class provides the templates for ownCloud. */ class OC_Template extends \OC\Template\Base { /** @var string */ private $renderAs; // Create a full page? /** @var string */ private $path; // The path to the template /** @var array */ private $headers = array(); //custom headers /** @var string */ protected $app; // app id protected static $initTemplateEngineFirstRun = true; /** * Constructor * * @param string $app app providing the template * @param string $name of the template file (without suffix) * @param string $renderAs If $renderAs is set, OC_Template will try to * produce a full page in the according layout. For * now, $renderAs can be set to "guest", "user" or * "admin". * @param bool $registerCall = true */ public function __construct( $app, $name, $renderAs = "", $registerCall = true ) { // Read the selected theme from the config file self::initTemplateEngine($renderAs); $theme = OC_Util::getTheme(); $requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : ''; $parts = explode('/', $app); // fix translation when app is something like core/lostpassword $l10n = \OC::$server->getL10N($parts[0]); /** @var \OCP\Defaults $themeDefaults */ $themeDefaults = \OC::$server->query(\OCP\Defaults::class); list($path, $template) = $this->findTemplate($theme, $app, $name); // Set the private data $this->renderAs = $renderAs; $this->path = $path; $this->app = $app; parent::__construct($template, $requestToken, $l10n, $themeDefaults); } /** * @param string $renderAs */ public static function initTemplateEngine($renderAs) { if (self::$initTemplateEngineFirstRun){ //apps that started before the template initialization can load their own scripts/styles //so to make sure this scripts/styles here are loaded first we use OC_Util::addScript() with $prepend=true //meaning the last script/style in this list will be loaded first if (\OC::$server->getSystemConfig()->getValue ('installed', false) && $renderAs !== 'error' && !\OCP\Util::needUpgrade()) { if (\OC::$server->getConfig ()->getAppValue ( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax') { OC_Util::addScript ( 'backgroundjobs', null, true ); } } OC_Util::addStyle('jquery-ui-fixes',null,true); OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true); OC_Util::addStyle('server', null, true); OC_Util::addVendorStyle('select2/select2', null, true); OC_Util::addStyle('jquery.ocdialog'); OC_Util::addTranslations("core", null, true); OC_Util::addScript('search', 'search', true); OC_Util::addScript('merged-template-prepend', null, true); OC_Util::addScript('jquery-ui-fixes'); OC_Util::addScript('files/fileinfo'); OC_Util::addScript('files/client'); OC_Util::addScript('contactsmenu'); if (\OC::$server->getConfig()->getSystemValue('debug')) { // Add the stuff we need always // following logic will import all vendor libraries that are // specified in core/js/core.json $fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json'); if($fileContent !== false) { $coreDependencies = json_decode($fileContent, true); foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) { //remove trailing ".js" as addVendorScript will append it OC_Util::addVendorScript( substr($vendorLibrary, 0, strlen($vendorLibrary) - 3),null,true); } } else { throw new \Exception('Cannot read core/js/core.json'); } } else { // Import all (combined) default vendor libraries OC_Util::addVendorScript('core', null, true); } if (\OC::$server->getRequest()->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE])) { // polyfill for btoa/atob for IE friends OC_Util::addVendorScript('base64/base64'); // shim for the davclient.js library \OCP\Util::addScript('files/iedavclient'); } self::$initTemplateEngineFirstRun = false; } } /** * find the template with the given name * @param string $name of the template file (without suffix) * * Will select the template file for the selected theme. * Checking all the possible locations. * @param string $theme * @param string $app * @return string[] */ protected function findTemplate($theme, $app, $name) { // Check if it is a app template or not. if( $app !== '' ) { $dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app)); } else { $dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT); } $locator = new \OC\Template\TemplateFileLocator( $dirs ); $template = $locator->find($name); $path = $locator->getPath(); return array($path, $template); } /** * Add a custom element to the header * @param string $tag tag name of the element * @param array $attributes array of attributes for the element * @param string $text the text content for the element. If $text is null then the * element will be written as empty element. So use "" to get a closing tag. */ public function addHeader($tag, $attributes, $text=null) { $this->headers[]= array( 'tag' => $tag, 'attributes' => $attributes, 'text' => $text ); } /** * Process the template * @return boolean|string * * This function process the template. If $this->renderAs is set, it * will produce a full page. */ public function fetchPage($additionalParams = null) { $data = parent::fetchPage($additionalParams); if( $this->renderAs ) { $page = new TemplateLayout($this->renderAs, $this->app); // Add custom headers $headers = ''; foreach(OC_Util::$headers as $header) { $headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']); foreach($header['attributes'] as $name=>$value) { $headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"'; } if ($header['text'] !== null) { $headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>'; } else { $headers .= '/>'; } } $page->assign('headers', $headers); $page->assign('content', $data); return $page->fetchPage(); } return $data; } /** * Include template * * @param string $file * @param array|null $additionalParams * @return string returns content of included template * * Includes another template. use <?php echo $this->inc('template'); ?> to * do this. */ public function inc( $file, $additionalParams = null ) { return $this->load($this->path.$file.'.php', $additionalParams); } /** * Shortcut to print a simple page for users * @param string $application The application we render the template for * @param string $name Name of the template * @param array $parameters Parameters for the template * @return boolean|null */ public static function printUserPage( $application, $name, $parameters = array() ) { $content = new OC_Template( $application, $name, "user" ); foreach( $parameters as $key => $value ) { $content->assign( $key, $value ); } print $content->printPage(); } /** * Shortcut to print a simple page for admins * @param string $application The application we render the template for * @param string $name Name of the template * @param array $parameters Parameters for the template * @return bool */ public static function printAdminPage( $application, $name, $parameters = array() ) { $content = new OC_Template( $application, $name, "admin" ); foreach( $parameters as $key => $value ) { $content->assign( $key, $value ); } return $content->printPage(); } /** * Shortcut to print a simple page for guests * @param string $application The application we render the template for * @param string $name Name of the template * @param array|string $parameters Parameters for the template * @return bool */ public static function printGuestPage( $application, $name, $parameters = array() ) { $content = new OC_Template( $application, $name, "guest" ); foreach( $parameters as $key => $value ) { $content->assign( $key, $value ); } return $content->printPage(); } /** * Print a fatal error page and terminates the script * @param string $error_msg The error message to show * @param string $hint An optional hint message - needs to be properly escaped */ public static function printErrorPage( $error_msg, $hint = '' ) { if (\OC_App::isEnabled('theming') && !\OC_App::isAppLoaded('theming')) { \OC_App::loadApp('theming'); } if ($error_msg === $hint) { // If the hint is the same as the message there is no need to display it twice. $hint = ''; } try { $content = new \OC_Template( '', 'error', 'error', false ); $errors = array(array('error' => $error_msg, 'hint' => $hint)); $content->assign( 'errors', $errors ); $content->printPage(); } catch (\Exception $e) { $logger = \OC::$server->getLogger(); $logger->error("$error_msg $hint", ['app' => 'core']); $logger->logException($e, ['app' => 'core']); header(self::getHttpProtocol() . ' 500 Internal Server Error'); header('Content-Type: text/plain; charset=utf-8'); print("$error_msg $hint"); } die(); } /** * print error page using Exception details * @param Exception | Throwable $exception */ public static function printExceptionErrorPage($exception, $fetchPage = false) { try { $request = \OC::$server->getRequest(); $content = new \OC_Template('', 'exception', 'error', false); $content->assign('errorClass', get_class($exception)); $content->assign('errorMsg', $exception->getMessage()); $content->assign('errorCode', $exception->getCode()); $content->assign('file', $exception->getFile()); $content->assign('line', $exception->getLine()); $content->assign('trace', $exception->getTraceAsString()); $content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false)); $content->assign('remoteAddr', $request->getRemoteAddress()); $content->assign('requestID', $request->getId()); if ($fetchPage) { return $content->fetchPage(); } $content->printPage(); } catch (\Exception $e) { $logger = \OC::$server->getLogger(); $logger->logException($exception, ['app' => 'core']); $logger->logException($e, ['app' => 'core']); header(self::getHttpProtocol() . ' 500 Internal Server Error'); header('Content-Type: text/plain; charset=utf-8'); print("Internal Server Error\n\n"); print("The server encountered an internal error and was unable to complete your request.\n"); print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); print("More details can be found in the server log.\n"); } die(); } /** * This is only here to reduce the dependencies in case of an exception to * still be able to print a plain error message. * * Returns the used HTTP protocol. * * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0. * @internal Don't use this - use AppFramework\Http\Request->getHttpProtocol instead */ protected static function getHttpProtocol() { $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']); $validProtocols = [ 'HTTP/1.0', 'HTTP/1.1', 'HTTP/2', ]; if(in_array($claimedProtocol, $validProtocols, true)) { return $claimedProtocol; } return 'HTTP/1.1'; } } private/legacy/defaults.php 0000604 00000020106 15247130452 0012000 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Pascal de Bruijn <pmjdebruijn@pcode.nl> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author scolebrook <scolebrook@mac.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Volkan Gezer <volkangezer@gmail.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_Defaults { private $theme; private $l; private $defaultEntity; private $defaultName; private $defaultTitle; private $defaultBaseUrl; private $defaultSyncClientUrl; private $defaultiOSClientUrl; private $defaultiTunesAppId; private $defaultAndroidClientUrl; private $defaultDocBaseUrl; private $defaultDocVersion; private $defaultSlogan; private $defaultLogoClaim; private $defaultColorPrimary; public function __construct() { $this->l = \OC::$server->getL10N('lib'); $this->defaultEntity = 'Nextcloud'; /* e.g. company name, used for footers and copyright notices */ $this->defaultName = 'Nextcloud'; /* short name, used when referring to the software */ $this->defaultTitle = 'Nextcloud'; /* can be a longer name, for titles */ $this->defaultBaseUrl = 'https://nextcloud.com'; $this->defaultSyncClientUrl = 'https://nextcloud.com/install/#install-clients'; $this->defaultiOSClientUrl = 'https://itunes.apple.com/us/app/nextcloud/id1125420102?mt=8'; $this->defaultiTunesAppId = '1125420102'; $this->defaultAndroidClientUrl = 'https://play.google.com/store/apps/details?id=com.nextcloud.client'; $this->defaultDocBaseUrl = 'https://docs.nextcloud.com'; $this->defaultDocVersion = '12'; // used to generate doc links $this->defaultSlogan = $this->l->t('a safe home for all your data'); $this->defaultLogoClaim = ''; $this->defaultColorPrimary = '#0082c9'; $themePath = OC::$SERVERROOT . '/themes/' . OC_Util::getTheme() . '/defaults.php'; if (file_exists($themePath)) { // prevent defaults.php from printing output ob_start(); require_once $themePath; ob_end_clean(); if (class_exists('OC_Theme')) { $this->theme = new OC_Theme(); } } } /** * @param string $method */ private function themeExist($method) { if (isset($this->theme) && method_exists($this->theme, $method)) { return true; } return false; } /** * Returns the base URL * @return string URL */ public function getBaseUrl() { if ($this->themeExist('getBaseUrl')) { return $this->theme->getBaseUrl(); } else { return $this->defaultBaseUrl; } } /** * Returns the URL where the sync clients are listed * @return string URL */ public function getSyncClientUrl() { if ($this->themeExist('getSyncClientUrl')) { return $this->theme->getSyncClientUrl(); } else { return $this->defaultSyncClientUrl; } } /** * Returns the URL to the App Store for the iOS Client * @return string URL */ public function getiOSClientUrl() { if ($this->themeExist('getiOSClientUrl')) { return $this->theme->getiOSClientUrl(); } else { return $this->defaultiOSClientUrl; } } /** * Returns the AppId for the App Store for the iOS Client * @return string AppId */ public function getiTunesAppId() { if ($this->themeExist('getiTunesAppId')) { return $this->theme->getiTunesAppId(); } else { return $this->defaultiTunesAppId; } } /** * Returns the URL to Google Play for the Android Client * @return string URL */ public function getAndroidClientUrl() { if ($this->themeExist('getAndroidClientUrl')) { return $this->theme->getAndroidClientUrl(); } else { return $this->defaultAndroidClientUrl; } } /** * Returns the documentation URL * @return string URL */ public function getDocBaseUrl() { if ($this->themeExist('getDocBaseUrl')) { return $this->theme->getDocBaseUrl(); } else { return $this->defaultDocBaseUrl; } } /** * Returns the title * @return string title */ public function getTitle() { if ($this->themeExist('getTitle')) { return $this->theme->getTitle(); } else { return $this->defaultTitle; } } /** * Returns the short name of the software * @return string title */ public function getName() { if ($this->themeExist('getName')) { return $this->theme->getName(); } else { return $this->defaultName; } } /** * Returns the short name of the software containing HTML strings * @return string title */ public function getHTMLName() { if ($this->themeExist('getHTMLName')) { return $this->theme->getHTMLName(); } else { return $this->defaultName; } } /** * Returns entity (e.g. company name) - used for footer, copyright * @return string entity name */ public function getEntity() { if ($this->themeExist('getEntity')) { return $this->theme->getEntity(); } else { return $this->defaultEntity; } } /** * Returns slogan * @return string slogan */ public function getSlogan() { if ($this->themeExist('getSlogan')) { return $this->theme->getSlogan(); } else { return $this->defaultSlogan; } } /** * Returns logo claim * @return string logo claim */ public function getLogoClaim() { if ($this->themeExist('getLogoClaim')) { return $this->theme->getLogoClaim(); } else { return $this->defaultLogoClaim; } } /** * Returns short version of the footer * @return string short footer */ public function getShortFooter() { if ($this->themeExist('getShortFooter')) { $footer = $this->theme->getShortFooter(); } else { $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' . ' rel="noreferrer">' .$this->getEntity() . '</a>'. ' – ' . $this->getSlogan(); } return $footer; } /** * Returns long version of the footer * @return string long footer */ public function getLongFooter() { if ($this->themeExist('getLongFooter')) { $footer = $this->theme->getLongFooter(); } else { $footer = $this->getShortFooter(); } return $footer; } /** * @param string $key * @return string URL to doc with key */ public function buildDocLinkToKey($key) { if ($this->themeExist('buildDocLinkToKey')) { return $this->theme->buildDocLinkToKey($key); } return $this->getDocBaseUrl() . '/server/' . $this->defaultDocVersion . '/go.php?to=' . $key; } /** * Returns primary color * @return string */ public function getColorPrimary() { if ($this->themeExist('getColorPrimary')) { return $this->theme->getColorPrimary(); } if ($this->themeExist('getMailHeaderColor')) { return $this->theme->getMailHeaderColor(); } return $this->defaultColorPrimary; } /** * @return array scss variables to overwrite */ public function getScssVariables() { if($this->themeExist('getScssVariables')) { return $this->theme->getScssVariables(); } return []; } public function shouldReplaceIcons() { return false; } /** * Themed logo url * * @param bool $useSvg Whether to point to the SVG image or a fallback * @return string */ public function getLogo($useSvg = true) { if ($this->themeExist('getLogo')) { return $this->theme->getLogo($useSvg); } if($useSvg) { $logo = \OC::$server->getURLGenerator()->imagePath('core', 'logo.svg'); } else { $logo = \OC::$server->getURLGenerator()->imagePath('core', 'logo.png'); } return $logo . '?v=' . hash('sha1', implode('.', \OCP\Util::getVersion())); } } private/legacy/filechunking.php 0000604 00000011325 15247130452 0012642 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Felix Moeller <mail@felixmoeller.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_FileChunking { protected $info; protected $cache; /** * TTL of chunks * * @var int */ protected $ttl; static public function decodeName($name) { preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches); return $matches; } /** * @param string[] $info */ public function __construct($info) { $this->info = $info; $this->ttl = \OC::$server->getConfig()->getSystemValue('cache_chunk_gc_ttl', 86400); } public function getPrefix() { $name = $this->info['name']; $transferid = $this->info['transferid']; return $name.'-chunking-'.$transferid.'-'; } protected function getCache() { if (!isset($this->cache)) { $this->cache = new \OC\Cache\File(); } return $this->cache; } /** * Stores the given $data under the given $key - the number of stored bytes is returned * * @param string $index * @param resource $data * @return int */ public function store($index, $data) { $cache = $this->getCache(); $name = $this->getPrefix().$index; $cache->set($name, $data, $this->ttl); return $cache->size($name); } public function isComplete() { $prefix = $this->getPrefix(); $cache = $this->getCache(); $chunkcount = (int)$this->info['chunkcount']; for($i=($chunkcount-1); $i >= 0; $i--) { if (!$cache->hasKey($prefix.$i)) { return false; } } return true; } /** * Assembles the chunks into the file specified by the path. * Chunks are deleted afterwards. * * @param resource $f target path * * @return integer assembled file size * * @throws \OC\InsufficientStorageException when file could not be fully * assembled due to lack of free space */ public function assemble($f) { $cache = $this->getCache(); $prefix = $this->getPrefix(); $count = 0; for ($i = 0; $i < $this->info['chunkcount']; $i++) { $chunk = $cache->get($prefix.$i); // remove after reading to directly save space $cache->remove($prefix.$i); $count += fwrite($f, $chunk); // let php release the memory to work around memory exhausted error with php 5.6 $chunk = null; } return $count; } /** * Returns the size of the chunks already present * @return integer size in bytes */ public function getCurrentSize() { $cache = $this->getCache(); $prefix = $this->getPrefix(); $total = 0; for ($i = 0; $i < $this->info['chunkcount']; $i++) { $total += $cache->size($prefix.$i); } return $total; } /** * Removes all chunks which belong to this transmission */ public function cleanup() { $cache = $this->getCache(); $prefix = $this->getPrefix(); for($i=0; $i < $this->info['chunkcount']; $i++) { $cache->remove($prefix.$i); } } /** * Removes one specific chunk * @param string $index */ public function remove($index) { $cache = $this->getCache(); $prefix = $this->getPrefix(); $cache->remove($prefix.$index); } /** * Assembles the chunks into the file specified by the path. * Also triggers the relevant hooks and proxies. * * @param \OC\Files\Storage\Storage $storage storage * @param string $path target path relative to the storage * @return bool true on success or false if file could not be created * * @throws \OC\ServerNotAvailableException */ public function file_assemble($storage, $path) { // use file_put_contents as method because that best matches what this function does if (\OC\Files\Filesystem::isValidPath($path)) { $target = $storage->fopen($path, 'w'); if ($target) { $count = $this->assemble($target); fclose($target); return $count > 0; } else { return false; } } return false; } } private/legacy/app.php 0000604 00000104211 15247130452 0010751 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Borjan Tchakaloff <borjan@tchakaloff.fr> * @author Brice Maron <brice@bmaron.net> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Felix Moeller <mail@felixmoeller.de> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Kamil Domanski <kdomanski@kdemail.net> * @author Klaas Freitag <freitag@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Markus Goetz <markus@woboq.com> * @author Morris Jobke <hey@morrisjobke.de> * @author RealRancor <Fisch.666@gmx.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sam Tuke <mail@samtuke.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Tom Needham <tom@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OC\App\DependencyAnalyzer; use OC\App\InfoParser; use OC\App\Platform; use OC\Installer; use OC\Repair; use OCP\App\ManagerEvent; /** * This class manages the apps. It allows them to register and integrate in the * ownCloud ecosystem. Furthermore, this class is responsible for installing, * upgrading and removing apps. */ class OC_App { static private $appVersion = []; static private $adminForms = array(); static private $personalForms = array(); static private $appInfo = array(); static private $appTypes = array(); static private $loadedApps = array(); static private $altLogin = array(); static private $alreadyRegistered = []; const officialApp = 200; /** * clean the appId * * @param string|boolean $app AppId that needs to be cleaned * @return string */ public static function cleanAppId($app) { return str_replace(array('\0', '/', '\\', '..'), '', $app); } /** * Check if an app is loaded * * @param string $app * @return bool */ public static function isAppLoaded($app) { return in_array($app, self::$loadedApps, true); } /** * loads all apps * * @param string[] | string | null $types * @return bool * * This function walks through the ownCloud directory and loads all apps * it can find. A directory contains an app if the file /appinfo/info.xml * exists. * * if $types is set, only apps of those types will be loaded */ public static function loadApps($types = null) { if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) { return false; } // Load the enabled apps here $apps = self::getEnabledApps(); // Add each apps' folder as allowed class path foreach($apps as $app) { $path = self::getAppPath($app); if($path !== false) { self::registerAutoloading($app, $path); } } // prevent app.php from printing output ob_start(); foreach ($apps as $app) { if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) { self::loadApp($app); } } ob_end_clean(); return true; } /** * load a single app * * @param string $app */ public static function loadApp($app) { self::$loadedApps[] = $app; $appPath = self::getAppPath($app); if($appPath === false) { return; } // in case someone calls loadApp() directly self::registerAutoloading($app, $appPath); if (is_file($appPath . '/appinfo/app.php')) { \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app); self::requireAppFile($app); if (self::isType($app, array('authentication'))) { // since authentication apps affect the "is app enabled for group" check, // the enabled apps cache needs to be cleared to make sure that the // next time getEnableApps() is called it will also include apps that were // enabled for groups self::$enabledAppsCache = array(); } \OC::$server->getEventLogger()->end('load_app_' . $app); } $info = self::getAppInfo($app); if (!empty($info['activity']['filters'])) { foreach ($info['activity']['filters'] as $filter) { \OC::$server->getActivityManager()->registerFilter($filter); } } if (!empty($info['activity']['settings'])) { foreach ($info['activity']['settings'] as $setting) { \OC::$server->getActivityManager()->registerSetting($setting); } } if (!empty($info['activity']['providers'])) { foreach ($info['activity']['providers'] as $provider) { \OC::$server->getActivityManager()->registerProvider($provider); } } } /** * @internal * @param string $app * @param string $path */ public static function registerAutoloading($app, $path) { $key = $app . '-' . $path; if(isset(self::$alreadyRegistered[$key])) { return; } self::$alreadyRegistered[$key] = true; // Register on PSR-4 composer autoloader $appNamespace = \OC\AppFramework\App::buildAppNamespace($app); \OC::$server->registerNamespace($app, $appNamespace); \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true); if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) { \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true); } // Register on legacy autoloader \OC::$loader->addValidRoot($path); } /** * Load app.php from the given app * * @param string $app app name */ private static function requireAppFile($app) { try { // encapsulated here to avoid variable scope conflicts require_once $app . '/appinfo/app.php'; } catch (Error $ex) { \OC::$server->getLogger()->logException($ex); $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps(); if (!in_array($app, $blacklist)) { self::disable($app); } } } /** * check if an app is of a specific type * * @param string $app * @param string|array $types * @return bool */ public static function isType($app, $types) { if (is_string($types)) { $types = array($types); } $appTypes = self::getAppTypes($app); foreach ($types as $type) { if (array_search($type, $appTypes) !== false) { return true; } } return false; } /** * get the types of an app * * @param string $app * @return array */ private static function getAppTypes($app) { //load the cache if (count(self::$appTypes) == 0) { self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types'); } if (isset(self::$appTypes[$app])) { return explode(',', self::$appTypes[$app]); } else { return array(); } } /** * read app types from info.xml and cache them in the database */ public static function setAppTypes($app) { $appData = self::getAppInfo($app); if(!is_array($appData)) { return; } if (isset($appData['types'])) { $appTypes = implode(',', $appData['types']); } else { $appTypes = ''; $appData['types'] = []; } \OC::$server->getAppConfig()->setValue($app, 'types', $appTypes); if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) { $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes'); if ($enabled !== 'yes' && $enabled !== 'no') { \OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes'); } } } /** * check if app is shipped * * @param string $appId the id of the app to check * @return bool * * Check if an app that is installed is a shipped app or installed from the appstore. */ public static function isShipped($appId) { return \OC::$server->getAppManager()->isShipped($appId); } /** * get all enabled apps */ protected static $enabledAppsCache = array(); /** * Returns apps enabled for the current user. * * @param bool $forceRefresh whether to refresh the cache * @param bool $all whether to return apps for all users, not only the * currently logged in one * @return string[] */ public static function getEnabledApps($forceRefresh = false, $all = false) { if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { return array(); } // in incognito mode or when logged out, $user will be false, // which is also the case during an upgrade $appManager = \OC::$server->getAppManager(); if ($all) { $user = null; } else { $user = \OC::$server->getUserSession()->getUser(); } if (is_null($user)) { $apps = $appManager->getInstalledApps(); } else { $apps = $appManager->getEnabledAppsForUser($user); } $apps = array_filter($apps, function ($app) { return $app !== 'files';//we add this manually }); sort($apps); array_unshift($apps, 'files'); return $apps; } /** * checks whether or not an app is enabled * * @param string $app app * @return bool * * This function checks whether or not an app is enabled. */ public static function isEnabled($app) { return \OC::$server->getAppManager()->isEnabledForUser($app); } /** * enables an app * * @param string $appId * @param array $groups (optional) when set, only these groups will have access to the app * @throws \Exception * @return void * * This function set an app as enabled in appconfig. */ public function enable($appId, $groups = null) { self::$enabledAppsCache = []; // flush // Check if app is already downloaded $installer = new Installer( \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), \OC::$server->getLogger(), \OC::$server->getConfig() ); $isDownloaded = $installer->isDownloaded($appId); if(!$isDownloaded) { $installer->downloadApp($appId); } $installer->installApp($appId); $appManager = \OC::$server->getAppManager(); if (!is_null($groups)) { $groupManager = \OC::$server->getGroupManager(); $groupsList = []; foreach ($groups as $group) { $groupItem = $groupManager->get($group); if ($groupItem instanceof \OCP\IGroup) { $groupsList[] = $groupManager->get($group); } } $appManager->enableAppForGroups($appId, $groupsList); } else { $appManager->enableApp($appId); } } /** * @param string $app * @return bool */ public static function removeApp($app) { if (self::isShipped($app)) { return false; } $installer = new Installer( \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), \OC::$server->getLogger(), \OC::$server->getConfig() ); return $installer->removeApp($app); } /** * This function set an app as disabled in appconfig. * * @param string $app app * @throws Exception */ public static function disable($app) { // flush self::$enabledAppsCache = array(); // run uninstall steps $appData = OC_App::getAppInfo($app); if (!is_null($appData)) { OC_App::executeRepairSteps($app, $appData['repair-steps']['uninstall']); } // emit disable hook - needed anymore ? \OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app)); // finally disable it $appManager = \OC::$server->getAppManager(); $appManager->disableApp($app); } // This is private as well. It simply works, so don't ask for more details private static function proceedNavigation($list) { usort($list, function($a, $b) { if (isset($a['order']) && isset($b['order'])) { return ($a['order'] < $b['order']) ? -1 : 1; } else if (isset($a['order']) || isset($b['order'])) { return isset($a['order']) ? -1 : 1; } else { return ($a['name'] < $b['name']) ? -1 : 1; } }); $activeApp = OC::$server->getNavigationManager()->getActiveEntry(); foreach ($list as $index => &$navEntry) { if ($navEntry['id'] == $activeApp) { $navEntry['active'] = true; } else { $navEntry['active'] = false; } } unset($navEntry); return $list; } /** * Get the path where to install apps * * @return string|false */ public static function getInstallPath() { if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) { return false; } foreach (OC::$APPSROOTS as $dir) { if (isset($dir['writable']) && $dir['writable'] === true) { return $dir['path']; } } \OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR); return null; } /** * search for an app in all app-directories * * @param string $appId * @return false|string */ public static function findAppInDirectories($appId) { $sanitizedAppId = self::cleanAppId($appId); if($sanitizedAppId !== $appId) { return false; } static $app_dir = array(); if (isset($app_dir[$appId])) { return $app_dir[$appId]; } $possibleApps = array(); foreach (OC::$APPSROOTS as $dir) { if (file_exists($dir['path'] . '/' . $appId)) { $possibleApps[] = $dir; } } if (empty($possibleApps)) { return false; } elseif (count($possibleApps) === 1) { $dir = array_shift($possibleApps); $app_dir[$appId] = $dir; return $dir; } else { $versionToLoad = array(); foreach ($possibleApps as $possibleApp) { $version = self::getAppVersionByPath($possibleApp['path']); if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) { $versionToLoad = array( 'dir' => $possibleApp, 'version' => $version, ); } } $app_dir[$appId] = $versionToLoad['dir']; return $versionToLoad['dir']; //TODO - write test } } /** * Get the directory for the given app. * If the app is defined in multiple directories, the first one is taken. (false if not found) * * @param string $appId * @return string|false */ public static function getAppPath($appId) { if ($appId === null || trim($appId) === '') { return false; } if (($dir = self::findAppInDirectories($appId)) != false) { return $dir['path'] . '/' . $appId; } return false; } /** * Get the path for the given app on the access * If the app is defined in multiple directories, the first one is taken. (false if not found) * * @param string $appId * @return string|false */ public static function getAppWebPath($appId) { if (($dir = self::findAppInDirectories($appId)) != false) { return OC::$WEBROOT . $dir['url'] . '/' . $appId; } return false; } /** * get the last version of the app from appinfo/info.xml * * @param string $appId * @param bool $useCache * @return string */ public static function getAppVersion($appId, $useCache = true) { if($useCache && isset(self::$appVersion[$appId])) { return self::$appVersion[$appId]; } $file = self::getAppPath($appId); self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0'; return self::$appVersion[$appId]; } /** * get app's version based on it's path * * @param string $path * @return string */ public static function getAppVersionByPath($path) { $infoFile = $path . '/appinfo/info.xml'; $appData = self::getAppInfo($infoFile, true); return isset($appData['version']) ? $appData['version'] : ''; } /** * Read all app metadata from the info.xml file * * @param string $appId id of the app or the path of the info.xml file * @param bool $path * @param string $lang * @return array|null * @note all data is read from info.xml, not just pre-defined fields */ public static function getAppInfo($appId, $path = false, $lang = null) { if ($path) { $file = $appId; } else { if ($lang === null && isset(self::$appInfo[$appId])) { return self::$appInfo[$appId]; } $appPath = self::getAppPath($appId); if($appPath === false) { return null; } $file = $appPath . '/appinfo/info.xml'; } $parser = new InfoParser(\OC::$server->getMemCacheFactory()->create('core.appinfo')); $data = $parser->parse($file); if (is_array($data)) { $data = OC_App::parseAppInfo($data, $lang); } if(isset($data['ocsid'])) { $storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid'); if($storedId !== '' && $storedId !== $data['ocsid']) { $data['ocsid'] = $storedId; } } if ($lang === null) { self::$appInfo[$appId] = $data; } return $data; } /** * Returns the navigation * * @return array * * This function returns an array containing all entries added. The * entries are sorted by the key 'order' ascending. Additional to the keys * given for each app the following keys exist: * - active: boolean, signals if the user is on this navigation entry */ public static function getNavigation() { $entries = OC::$server->getNavigationManager()->getAll(); return self::proceedNavigation($entries); } /** * Returns the Settings Navigation * * @return string[] * * This function returns an array containing all settings pages added. The * entries are sorted by the key 'order' ascending. */ public static function getSettingsNavigation() { $entries = OC::$server->getNavigationManager()->getAll('settings'); return self::proceedNavigation($entries); } /** * get the id of loaded app * * @return string */ public static function getCurrentApp() { $request = \OC::$server->getRequest(); $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1); $topFolder = substr($script, 0, strpos($script, '/')); if (empty($topFolder)) { $path_info = $request->getPathInfo(); if ($path_info) { $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1); } } if ($topFolder == 'apps') { $length = strlen($topFolder); return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1); } else { return $topFolder; } } /** * @param string $type * @return array */ public static function getForms($type) { $forms = array(); switch ($type) { case 'admin': $source = self::$adminForms; break; case 'personal': $source = self::$personalForms; break; default: return array(); } foreach ($source as $form) { $forms[] = include $form; } return $forms; } /** * register an admin form to be shown * * @param string $app * @param string $page */ public static function registerAdmin($app, $page) { self::$adminForms[] = $app . '/' . $page . '.php'; } /** * register a personal form to be shown * @param string $app * @param string $page */ public static function registerPersonal($app, $page) { self::$personalForms[] = $app . '/' . $page . '.php'; } /** * @param array $entry */ public static function registerLogIn(array $entry) { self::$altLogin[] = $entry; } /** * @return array */ public static function getAlternativeLogIns() { return self::$altLogin; } /** * get a list of all apps in the apps folder * * @return array an array of app names (string IDs) * @todo: change the name of this method to getInstalledApps, which is more accurate */ public static function getAllApps() { $apps = array(); foreach (OC::$APPSROOTS as $apps_dir) { if (!is_readable($apps_dir['path'])) { \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN); continue; } $dh = opendir($apps_dir['path']); if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) { $apps[] = $file; } } } } return $apps; } /** * List all apps, this is used in apps.php * * @return array */ public function listAllApps() { $installedApps = OC_App::getAllApps(); //we don't want to show configuration for these $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps(); $appList = array(); $langCode = \OC::$server->getL10N('core')->getLanguageCode(); $urlGenerator = \OC::$server->getURLGenerator(); foreach ($installedApps as $app) { if (array_search($app, $blacklist) === false) { $info = OC_App::getAppInfo($app, false, $langCode); if (!is_array($info)) { \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR); continue; } if (!isset($info['name'])) { \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR); continue; } $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no'); $info['groups'] = null; if ($enabled === 'yes') { $active = true; } else if ($enabled === 'no') { $active = false; } else { $active = true; $info['groups'] = $enabled; } $info['active'] = $active; if (self::isShipped($app)) { $info['internal'] = true; $info['level'] = self::officialApp; $info['removable'] = false; } else { $info['internal'] = false; $info['removable'] = true; } $appPath = self::getAppPath($app); if($appPath !== false) { $appIcon = $appPath . '/img/' . $app . '.svg'; if (file_exists($appIcon)) { $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app . '.svg'); $info['previewAsIcon'] = true; } else { $appIcon = $appPath . '/img/app.svg'; if (file_exists($appIcon)) { $info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, 'app.svg'); $info['previewAsIcon'] = true; } } } // fix documentation if (isset($info['documentation']) && is_array($info['documentation'])) { foreach ($info['documentation'] as $key => $url) { // If it is not an absolute URL we assume it is a key // i.e. admin-ldap will get converted to go.php?to=admin-ldap if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) { $url = $urlGenerator->linkToDocs($url); } $info['documentation'][$key] = $url; } } $info['version'] = OC_App::getAppVersion($app); $appList[] = $info; } } return $appList; } /** * Returns the internal app ID or false * @param string $ocsID * @return string|false */ public static function getInternalAppIdByOcs($ocsID) { if(is_numeric($ocsID)) { $idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid'); if(array_search($ocsID, $idArray)) { return array_search($ocsID, $idArray); } } return false; } public static function shouldUpgrade($app) { $versions = self::getAppVersions(); $currentVersion = OC_App::getAppVersion($app); if ($currentVersion && isset($versions[$app])) { $installedVersion = $versions[$app]; if (!version_compare($currentVersion, $installedVersion, '=')) { return true; } } return false; } /** * Adjust the number of version parts of $version1 to match * the number of version parts of $version2. * * @param string $version1 version to adjust * @param string $version2 version to take the number of parts from * @return string shortened $version1 */ private static function adjustVersionParts($version1, $version2) { $version1 = explode('.', $version1); $version2 = explode('.', $version2); // reduce $version1 to match the number of parts in $version2 while (count($version1) > count($version2)) { array_pop($version1); } // if $version1 does not have enough parts, add some while (count($version1) < count($version2)) { $version1[] = '0'; } return implode('.', $version1); } /** * Check whether the current ownCloud version matches the given * application's version requirements. * * The comparison is made based on the number of parts that the * app info version has. For example for ownCloud 6.0.3 if the * app info version is expecting version 6.0, the comparison is * made on the first two parts of the ownCloud version. * This means that it's possible to specify "requiremin" => 6 * and "requiremax" => 6 and it will still match ownCloud 6.0.3. * * @param string $ocVersion ownCloud version to check against * @param array $appInfo app info (from xml) * * @return boolean true if compatible, otherwise false */ public static function isAppCompatible($ocVersion, $appInfo) { $requireMin = ''; $requireMax = ''; if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) { $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version']; } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) { $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version']; } else if (isset($appInfo['requiremin'])) { $requireMin = $appInfo['requiremin']; } else if (isset($appInfo['require'])) { $requireMin = $appInfo['require']; } if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) { $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version']; } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) { $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version']; } else if (isset($appInfo['requiremax'])) { $requireMax = $appInfo['requiremax']; } if (is_array($ocVersion)) { $ocVersion = implode('.', $ocVersion); } if (!empty($requireMin) && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<') ) { return false; } if (!empty($requireMax) && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>') ) { return false; } return true; } /** * get the installed version of all apps */ public static function getAppVersions() { static $versions; if(!$versions) { $appConfig = \OC::$server->getAppConfig(); $versions = $appConfig->getValues(false, 'installed_version'); } return $versions; } /** * @param string $app * @param \OCP\IConfig $config * @param \OCP\IL10N $l * @return bool * * @throws Exception if app is not compatible with this version of ownCloud * @throws Exception if no app-name was specified */ public function installApp($app, \OCP\IConfig $config, \OCP\IL10N $l) { if ($app !== false) { // check if the app is compatible with this version of ownCloud $info = self::getAppInfo($app); if(!is_array($info)) { throw new \Exception( $l->t('App "%s" cannot be installed because appinfo file cannot be read.', [$info['name']] ) ); } $version = \OCP\Util::getVersion(); if (!self::isAppCompatible($version, $info)) { throw new \Exception( $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.', array($info['name']) ) ); } // check for required dependencies self::checkAppDependencies($config, $l, $info); $config->setAppValue($app, 'enabled', 'yes'); if (isset($appData['id'])) { $config->setAppValue($app, 'ocsid', $appData['id']); } if(isset($info['settings']) && is_array($info['settings'])) { $appPath = self::getAppPath($app); self::registerAutoloading($app, $appPath); \OC::$server->getSettingsManager()->setupSettings($info['settings']); } \OC_Hook::emit('OC_App', 'post_enable', array('app' => $app)); } else { if(empty($appName) ) { throw new \Exception($l->t("No app name specified")); } else { throw new \Exception($l->t("App '%s' could not be installed!", $appName)); } } return $app; } /** * update the database for the app and call the update script * * @param string $appId * @return bool */ public static function updateApp($appId) { $appPath = self::getAppPath($appId); if($appPath === false) { return false; } $appData = self::getAppInfo($appId); self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']); if (file_exists($appPath . '/appinfo/database.xml')) { OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml'); } self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']); self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']); unset(self::$appVersion[$appId]); // run upgrade code if (file_exists($appPath . '/appinfo/update.php')) { self::loadApp($appId); include $appPath . '/appinfo/update.php'; } self::registerAutoloading($appId, $appPath); self::setupBackgroundJobs($appData['background-jobs']); if(isset($appData['settings']) && is_array($appData['settings'])) { \OC::$server->getSettingsManager()->setupSettings($appData['settings']); } //set remote/public handlers if (array_key_exists('ocsid', $appData)) { \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']); } elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) { \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid'); } foreach ($appData['remote'] as $name => $path) { \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path); } foreach ($appData['public'] as $name => $path) { \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path); } self::setAppTypes($appId); $version = \OC_App::getAppVersion($appId); \OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version); \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent( ManagerEvent::EVENT_APP_UPDATE, $appId )); return true; } /** * @param string $appId * @param string[] $steps * @throws \OC\NeedsUpdateException */ public static function executeRepairSteps($appId, array $steps) { if (empty($steps)) { return; } // load the app self::loadApp($appId); $dispatcher = OC::$server->getEventDispatcher(); // load the steps $r = new Repair([], $dispatcher); foreach ($steps as $step) { try { $r->addStep($step); } catch (Exception $ex) { $r->emit('\OC\Repair', 'error', [$ex->getMessage()]); \OC::$server->getLogger()->logException($ex); } } // run the steps $r->run(); } public static function setupBackgroundJobs(array $jobs) { $queue = \OC::$server->getJobList(); foreach ($jobs as $job) { $queue->add($job); } } /** * @param string $appId * @param string[] $steps */ private static function setupLiveMigrations($appId, array $steps) { $queue = \OC::$server->getJobList(); foreach ($steps as $step) { $queue->add('OC\Migration\BackgroundRepair', [ 'app' => $appId, 'step' => $step]); } } /** * @param string $appId * @return \OC\Files\View|false */ public static function getStorage($appId) { if (OC_App::isEnabled($appId)) { //sanity check if (\OC::$server->getUserSession()->isLoggedIn()) { $view = new \OC\Files\View('/' . OC_User::getUser()); if (!$view->file_exists($appId)) { $view->mkdir($appId); } return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId); } else { \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR); return false; } } else { \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR); return false; } } protected static function findBestL10NOption($options, $lang) { $fallback = $similarLangFallback = $englishFallback = false; $lang = strtolower($lang); $similarLang = $lang; if (strpos($similarLang, '_')) { // For "de_DE" we want to find "de" and the other way around $similarLang = substr($lang, 0, strpos($lang, '_')); } foreach ($options as $option) { if (is_array($option)) { if ($fallback === false) { $fallback = $option['@value']; } if (!isset($option['@attributes']['lang'])) { continue; } $attributeLang = strtolower($option['@attributes']['lang']); if ($attributeLang === $lang) { return $option['@value']; } if ($attributeLang === $similarLang) { $similarLangFallback = $option['@value']; } else if (strpos($attributeLang, $similarLang . '_') === 0) { if ($similarLangFallback === false) { $similarLangFallback = $option['@value']; } } } else { $englishFallback = $option; } } if ($similarLangFallback !== false) { return $similarLangFallback; } else if ($englishFallback !== false) { return $englishFallback; } return (string) $fallback; } /** * parses the app data array and enhanced the 'description' value * * @param array $data the app data * @param string $lang * @return array improved app data */ public static function parseAppInfo(array $data, $lang = null) { if ($lang && isset($data['name']) && is_array($data['name'])) { $data['name'] = self::findBestL10NOption($data['name'], $lang); } if ($lang && isset($data['summary']) && is_array($data['summary'])) { $data['summary'] = self::findBestL10NOption($data['summary'], $lang); } if ($lang && isset($data['description']) && is_array($data['description'])) { $data['description'] = trim(self::findBestL10NOption($data['description'], $lang)); } else if (isset($data['description']) && is_string($data['description'])) { $data['description'] = trim($data['description']); } else { $data['description'] = ''; } return $data; } /** * @param \OCP\IConfig $config * @param \OCP\IL10N $l * @param array $info * @throws \Exception */ public static function checkAppDependencies($config, $l, $info) { $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l); $missing = $dependencyAnalyzer->analyze($info); if (!empty($missing)) { $missingMsg = join(PHP_EOL, $missing); throw new \Exception( $l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s', [$info['name'], $missingMsg] ) ); } } } private/legacy/db/statementwrapper.php 0000604 00000006601 15247130452 0014167 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement * * @method boolean bindValue(mixed $param, mixed $value, integer $type = null); * @method string errorCode(); * @method array errorInfo(); * @method integer rowCount(); * @method array fetchAll(integer $fetchMode = null); */ class OC_DB_StatementWrapper { /** * @var \Doctrine\DBAL\Driver\Statement */ private $statement = null; private $isManipulation = false; private $lastArguments = array(); /** * @param boolean $isManipulation */ public function __construct($statement, $isManipulation) { $this->statement = $statement; $this->isManipulation = $isManipulation; } /** * pass all other function directly to the \Doctrine\DBAL\Driver\Statement */ public function __call($name,$arguments) { return call_user_func_array(array($this->statement,$name), $arguments); } /** * make execute return the result instead of a bool * * @param array $input * @return \OC_DB_StatementWrapper|int */ public function execute($input= []) { $this->lastArguments = $input; if (count($input) > 0) { $result = $this->statement->execute($input); } else { $result = $this->statement->execute(); } if ($result === false) { return false; } if ($this->isManipulation) { $count = $this->statement->rowCount(); return $count; } else { return $this; } } /** * provide an alias for fetch * * @return mixed */ public function fetchRow() { return $this->statement->fetch(); } /** * Provide a simple fetchOne. * * fetch single column from the next row * @param int $column the column number to fetch * @return string */ public function fetchOne($column = 0) { return $this->statement->fetchColumn($column); } /** * Binds a PHP variable to a corresponding named or question mark placeholder in the * SQL statement that was use to prepare the statement. * * @param mixed $column Either the placeholder name or the 1-indexed placeholder index * @param mixed $variable The variable to bind * @param integer|null $type one of the PDO::PARAM_* constants * @param integer|null $length max length when using an OUT bind * @return boolean */ public function bindParam($column, &$variable, $type = null, $length = null){ return $this->statement->bindParam($column, $variable, $type, $length); } } private/legacy/db.php 0000604 00000016757 15247130452 0010577 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * This class manages the access to the database. It basically is a wrapper for * Doctrine with some adaptions. */ class OC_DB { /** * get MDB2 schema manager * * @return \OC\DB\MDB2SchemaManager */ private static function getMDB2SchemaManager() { return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection()); } /** * Prepare a SQL query * @param string $query Query string * @param int $limit * @param int $offset * @param bool $isManipulation * @throws \OC\DatabaseException * @return OC_DB_StatementWrapper prepared SQL query * * SQL query via Doctrine prepare(), needs to be execute()'d! */ static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) { $connection = \OC::$server->getDatabaseConnection(); if ($isManipulation === null) { //try to guess, so we return the number of rows on manipulations $isManipulation = self::isManipulation($query); } // return the result try { $result =$connection->prepare($query, $limit, $offset); } catch (\Doctrine\DBAL\DBALException $e) { throw new \OC\DatabaseException($e->getMessage(), $query); } // differentiate between query and manipulation $result = new OC_DB_StatementWrapper($result, $isManipulation); return $result; } /** * tries to guess the type of statement based on the first 10 characters * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements * * @param string $sql * @return bool */ static public function isManipulation( $sql ) { $selectOccurrence = stripos($sql, 'SELECT'); if ($selectOccurrence !== false && $selectOccurrence < 10) { return false; } $insertOccurrence = stripos($sql, 'INSERT'); if ($insertOccurrence !== false && $insertOccurrence < 10) { return true; } $updateOccurrence = stripos($sql, 'UPDATE'); if ($updateOccurrence !== false && $updateOccurrence < 10) { return true; } $deleteOccurrence = stripos($sql, 'DELETE'); if ($deleteOccurrence !== false && $deleteOccurrence < 10) { return true; } return false; } /** * execute a prepared statement, on error write log and throw exception * @param mixed $stmt OC_DB_StatementWrapper, * an array with 'sql' and optionally 'limit' and 'offset' keys * .. or a simple sql query string * @param array $parameters * @return OC_DB_StatementWrapper * @throws \OC\DatabaseException */ static public function executeAudited( $stmt, array $parameters = null) { if (is_string($stmt)) { // convert to an array with 'sql' if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT // TODO try to convert LIMIT OFFSET notation to parameters $message = 'LIMIT and OFFSET are forbidden for portability reasons,' . ' pass an array with \'limit\' and \'offset\' instead'; throw new \OC\DatabaseException($message); } $stmt = array('sql' => $stmt, 'limit' => null, 'offset' => null); } if (is_array($stmt)) { // convert to prepared statement if ( ! array_key_exists('sql', $stmt) ) { $message = 'statement array must at least contain key \'sql\''; throw new \OC\DatabaseException($message); } if ( ! array_key_exists('limit', $stmt) ) { $stmt['limit'] = null; } if ( ! array_key_exists('limit', $stmt) ) { $stmt['offset'] = null; } $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']); } self::raiseExceptionOnError($stmt, 'Could not prepare statement'); if ($stmt instanceof OC_DB_StatementWrapper) { $result = $stmt->execute($parameters); self::raiseExceptionOnError($result, 'Could not execute statement'); } else { if (is_object($stmt)) { $message = 'Expected a prepared statement or array got ' . get_class($stmt); } else { $message = 'Expected a prepared statement or array got ' . gettype($stmt); } throw new \OC\DatabaseException($message); } return $result; } /** * saves database schema to xml file * @param string $file name of file * @param int $mode * @return bool * * TODO: write more documentation */ public static function getDbStructure($file) { $schemaManager = self::getMDB2SchemaManager(); return $schemaManager->getDbStructure($file); } /** * Creates tables from XML file * @param string $file file to read structure from * @return bool * * TODO: write more documentation */ public static function createDbFromStructure( $file ) { $schemaManager = self::getMDB2SchemaManager(); $result = $schemaManager->createDbFromStructure($file); return $result; } /** * update the database schema * @param string $file file to read structure from * @throws Exception * @return string|boolean */ public static function updateDbFromStructure($file) { $schemaManager = self::getMDB2SchemaManager(); try { $result = $schemaManager->updateDbFromStructure($file); } catch (Exception $e) { \OCP\Util::writeLog('core', 'Failed to update database structure ('.$e.')', \OCP\Util::FATAL); throw $e; } return $result; } /** * remove all tables defined in a database structure xml file * @param string $file the xml file describing the tables */ public static function removeDBStructure($file) { $schemaManager = self::getMDB2SchemaManager(); $schemaManager->removeDBStructure($file); } /** * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException * @param mixed $result * @param string $message * @return void * @throws \OC\DatabaseException */ public static function raiseExceptionOnError($result, $message = null) { if($result === false) { if ($message === null) { $message = self::getErrorMessage(); } else { $message .= ', Root cause:' . self::getErrorMessage(); } throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode()); } } /** * returns the error code and message as a string for logging * works with DoctrineException * @return string */ public static function getErrorMessage() { $connection = \OC::$server->getDatabaseConnection(); return $connection->getError(); } /** * Checks if a table exists in the database - the database prefix will be prepended * * @param string $table * @return bool * @throws \OC\DatabaseException */ public static function tableExists($table) { $connection = \OC::$server->getDatabaseConnection(); return $connection->tableExists($table); } } private/legacy/helper.php 0000604 00000044417 15247130452 0011463 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Clark Tomlinson <fallen013@gmail.com> * @author Fabian Henze <flyser42@gmx.de> * @author Felix Moeller <mail@felixmoeller.de> * @author Georg Ehrke <georg@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Pellaeon Lin <nfsmwlin@gmail.com> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Simon Könnecke <simonkoennecke@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use Symfony\Component\Process\ExecutableFinder; /** * Collection of useful functions */ class OC_Helper { private static $templateManager; /** * Creates an absolute url for public use * @param string $service id * @param bool $add_slash * @return string the url * * Returns a absolute url to the given service. */ public static function linkToPublic($service, $add_slash = false) { if ($service === 'files') { $url = OC::$server->getURLGenerator()->getAbsoluteURL('/s'); } else { $url = OC::$server->getURLGenerator()->getAbsoluteURL(OC::$server->getURLGenerator()->linkTo('', 'public.php').'?service='.$service); } return $url . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : ''); } /** * Make a human file size * @param int $bytes file size in bytes * @return string a human readable file size * * Makes 2048 to 2 kB. */ public static function humanFileSize($bytes) { if ($bytes < 0) { return "?"; } if ($bytes < 1024) { return "$bytes B"; } $bytes = round($bytes / 1024, 0); if ($bytes < 1024) { return "$bytes KB"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return "$bytes MB"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return "$bytes GB"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return "$bytes TB"; } $bytes = round($bytes / 1024, 1); return "$bytes PB"; } /** * Make a php file size * @param int $bytes file size in bytes * @return string a php parseable file size * * Makes 2048 to 2k and 2^41 to 2048G */ public static function phpFileSize($bytes) { if ($bytes < 0) { return "?"; } if ($bytes < 1024) { return $bytes . "B"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return $bytes . "K"; } $bytes = round($bytes / 1024, 1); if ($bytes < 1024) { return $bytes . "M"; } $bytes = round($bytes / 1024, 1); return $bytes . "G"; } /** * Make a computer file size * @param string $str file size in human readable format * @return float a file size in bytes * * Makes 2kB to 2048. * * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418 */ public static function computerFileSize($str) { $str = strtolower($str); if (is_numeric($str)) { return floatval($str); } $bytes_array = array( 'b' => 1, 'k' => 1024, 'kb' => 1024, 'mb' => 1024 * 1024, 'm' => 1024 * 1024, 'gb' => 1024 * 1024 * 1024, 'g' => 1024 * 1024 * 1024, 'tb' => 1024 * 1024 * 1024 * 1024, 't' => 1024 * 1024 * 1024 * 1024, 'pb' => 1024 * 1024 * 1024 * 1024 * 1024, 'p' => 1024 * 1024 * 1024 * 1024 * 1024, ); $bytes = floatval($str); if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) { $bytes *= $bytes_array[$matches[1]]; } else { return false; } $bytes = round($bytes); return $bytes; } /** * Recursive copying of folders * @param string $src source folder * @param string $dest target folder * */ static function copyr($src, $dest) { if (is_dir($src)) { if (!is_dir($dest)) { mkdir($dest); } $files = scandir($src); foreach ($files as $file) { if ($file != "." && $file != "..") { self::copyr("$src/$file", "$dest/$file"); } } } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) { copy($src, $dest); } } /** * Recursive deletion of folders * @param string $dir path to the folder * @param bool $deleteSelf if set to false only the content of the folder will be deleted * @return bool */ static function rmdirr($dir, $deleteSelf = true) { if (is_dir($dir)) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($files as $fileInfo) { /** @var SplFileInfo $fileInfo */ if ($fileInfo->isLink()) { unlink($fileInfo->getPathname()); } else if ($fileInfo->isDir()) { rmdir($fileInfo->getRealPath()); } else { unlink($fileInfo->getRealPath()); } } if ($deleteSelf) { rmdir($dir); } } elseif (file_exists($dir)) { if ($deleteSelf) { unlink($dir); } } if (!$deleteSelf) { return true; } return !file_exists($dir); } /** * @return \OC\Files\Type\TemplateManager */ static public function getFileTemplateManager() { if (!self::$templateManager) { self::$templateManager = new \OC\Files\Type\TemplateManager(); } return self::$templateManager; } /** * detect if a given program is found in the search PATH * * @param string $name * @param bool $path * @internal param string $program name * @internal param string $optional search path, defaults to $PATH * @return bool true if executable program found in path */ public static function canExecute($name, $path = false) { // path defaults to PATH from environment if not set if ($path === false) { $path = getenv("PATH"); } // we look for an executable file of that name $exts = [""]; $check_fn = "is_executable"; // Default check will be done with $path directories : $dirs = explode(PATH_SEPARATOR, $path); // WARNING : We have to check if open_basedir is enabled : $obd = OC::$server->getIniWrapper()->getString('open_basedir'); if ($obd != "none") { $obd_values = explode(PATH_SEPARATOR, $obd); if (count($obd_values) > 0 and $obd_values[0]) { // open_basedir is in effect ! // We need to check if the program is in one of these dirs : $dirs = $obd_values; } } foreach ($dirs as $dir) { foreach ($exts as $ext) { if ($check_fn("$dir/$name" . $ext)) return true; } } return false; } /** * copy the contents of one stream to another * * @param resource $source * @param resource $target * @return array the number of bytes copied and result */ public static function streamCopy($source, $target) { if (!$source or !$target) { return array(0, false); } $bufSize = 8192; $result = true; $count = 0; while (!feof($source)) { $buf = fread($source, $bufSize); $bytesWritten = fwrite($target, $buf); if ($bytesWritten !== false) { $count += $bytesWritten; } // note: strlen is expensive so only use it when necessary, // on the last block if ($bytesWritten === false || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf)) ) { // write error, could be disk full ? $result = false; break; } } return array($count, $result); } /** * Adds a suffix to the name in case the file exists * * @param string $path * @param string $filename * @return string */ public static function buildNotExistingFileName($path, $filename) { $view = \OC\Files\Filesystem::getView(); return self::buildNotExistingFileNameForView($path, $filename, $view); } /** * Adds a suffix to the name in case the file exists * * @param string $path * @param string $filename * @return string */ public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) { if ($path === '/') { $path = ''; } if ($pos = strrpos($filename, '.')) { $name = substr($filename, 0, $pos); $ext = substr($filename, $pos); } else { $name = $filename; $ext = ''; } $newpath = $path . '/' . $filename; if ($view->file_exists($newpath)) { if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) { //Replace the last "(number)" with "(number+1)" $last_match = count($matches[0]) - 1; $counter = $matches[1][$last_match][0] + 1; $offset = $matches[0][$last_match][1]; $match_length = strlen($matches[0][$last_match][0]); } else { $counter = 2; $match_length = 0; $offset = false; } do { if ($offset) { //Replace the last "(number)" with "(number+1)" $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length); } else { $newname = $name . ' (' . $counter . ')'; } $newpath = $path . '/' . $newname . $ext; $counter++; } while ($view->file_exists($newpath)); } return $newpath; } /** * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. * * @param array $input The array to work on * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 * @return array * * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. * based on http://www.php.net/manual/en/function.array-change-key-case.php#107715 * */ public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER; $ret = array(); foreach ($input as $k => $v) { $ret[mb_convert_case($k, $case, $encoding)] = $v; } return $ret; } /** * performs a search in a nested array * @param array $haystack the array to be searched * @param string $needle the search string * @param string $index optional, only search this key name * @return mixed the key of the matching field, otherwise false * * performs a search in a nested array * * taken from http://www.php.net/manual/en/function.array-search.php#97645 */ public static function recursiveArraySearch($haystack, $needle, $index = null) { $aIt = new RecursiveArrayIterator($haystack); $it = new RecursiveIteratorIterator($aIt); while ($it->valid()) { if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) { return $aIt->key(); } $it->next(); } return false; } /** * calculates the maximum upload size respecting system settings, free space and user quota * * @param string $dir the current folder where the user currently operates * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly * @return int number of bytes representing */ public static function maxUploadFilesize($dir, $freeSpace = null) { if (is_null($freeSpace) || $freeSpace < 0){ $freeSpace = self::freeSpace($dir); } return min($freeSpace, self::uploadLimit()); } /** * Calculate free space left within user quota * * @param string $dir the current folder where the user currently operates * @return int number of bytes representing */ public static function freeSpace($dir) { $freeSpace = \OC\Files\Filesystem::free_space($dir); if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { $freeSpace = max($freeSpace, 0); return $freeSpace; } else { return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 } } /** * Calculate PHP upload limit * * @return int PHP upload file size limit */ public static function uploadLimit() { $ini = \OC::$server->getIniWrapper(); $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize')); $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size')); if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) { return INF; } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) { return max($upload_max_filesize, $post_max_size); //only the non 0 value counts } else { return min($upload_max_filesize, $post_max_size); } } /** * Checks if a function is available * * @param string $function_name * @return bool */ public static function is_function_enabled($function_name) { if (!function_exists($function_name)) { return false; } $ini = \OC::$server->getIniWrapper(); $disabled = explode(',', $ini->get('disable_functions')); $disabled = array_map('trim', $disabled); if (in_array($function_name, $disabled)) { return false; } $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist')); $disabled = array_map('trim', $disabled); if (in_array($function_name, $disabled)) { return false; } return true; } /** * Try to find a program * * @param string $program * @return null|string */ public static function findBinaryPath($program) { $memcache = \OC::$server->getMemCacheFactory()->create('findBinaryPath'); if ($memcache->hasKey($program)) { return $memcache->get($program); } $result = null; if (self::is_function_enabled('exec')) { $exeSniffer = new ExecutableFinder(); // Returns null if nothing is found $result = $exeSniffer->find($program); if (empty($result)) { $paths = getenv('PATH'); if (empty($paths)) { $paths = '/usr/local/bin /usr/bin /opt/bin /bin'; } else { $paths = str_replace(':',' ',getenv('PATH')); } $command = 'find ' . $paths . ' -name ' . escapeshellarg($program) . ' 2> /dev/null'; exec($command, $output, $returnCode); if (count($output) > 0) { $result = escapeshellcmd($output[0]); } } } // store the value for 5 minutes $memcache->set($program, $result, 300); return $result; } /** * Calculate the disc space for the given path * * @param string $path * @param \OCP\Files\FileInfo $rootInfo (optional) * @return array * @throws \OCP\Files\NotFoundException */ public static function getStorageInfo($path, $rootInfo = null) { // return storage info without adding mount points $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false); if (!$rootInfo) { $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false); } if (!$rootInfo instanceof \OCP\Files\FileInfo) { throw new \OCP\Files\NotFoundException(); } $used = $rootInfo->getSize(); if ($used < 0) { $used = 0; } $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED; $storage = $rootInfo->getStorage(); $sourceStorage = $storage; if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { $includeExtStorage = false; $sourceStorage = $storage->getSourceStorage(); } if ($includeExtStorage) { if ($storage->instanceOfStorage('\OC\Files\Storage\Home') || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage') ) { /** @var \OC\Files\Storage\Home $storage */ $user = $storage->getUser(); } else { $user = \OC::$server->getUserSession()->getUser()->getUID(); } if ($user) { $quota = OC_Util::getUserQuota($user); } else { $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED; } if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) { // always get free space / total space from root + mount points return self::getGlobalStorageInfo(); } } // TODO: need a better way to get total space from storage if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) { /** @var \OC\Files\Storage\Wrapper\Quota $storage */ $quota = $sourceStorage->getQuota(); } $free = $sourceStorage->free_space($rootInfo->getInternalPath()); if ($free >= 0) { $total = $free + $used; } else { $total = $free; //either unknown or unlimited } if ($total > 0) { if ($quota > 0 && $total > $quota) { $total = $quota; } // prevent division by zero or error codes (negative values) $relative = round(($used / $total) * 10000) / 100; } else { $relative = 0; } $ownerId = $storage->getOwner($path); $ownerDisplayName = ''; $owner = \OC::$server->getUserManager()->get($ownerId); if($owner) { $ownerDisplayName = $owner->getDisplayName(); } return [ 'free' => $free, 'used' => $used, 'quota' => $quota, 'total' => $total, 'relative' => $relative, 'owner' => $ownerId, 'ownerDisplayName' => $ownerDisplayName, ]; } /** * Get storage info including all mount points and quota * * @return array */ private static function getGlobalStorageInfo() { $quota = OC_Util::getUserQuota(\OCP\User::getUser()); $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext'); $used = $rootInfo['size']; if ($used < 0) { $used = 0; } $total = $quota; $free = $quota - $used; if ($total > 0) { if ($quota > 0 && $total > $quota) { $total = $quota; } // prevent division by zero or error codes (negative values) $relative = round(($used / $total) * 10000) / 100; } else { $relative = 0; } return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative); } /** * Returns whether the config file is set manually to read-only * @return bool */ public static function isReadOnlyConfigEnabled() { return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false); } } private/legacy/util.php 0000604 00000136043 15247130452 0011156 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Adam Williamson <awilliam@redhat.com> * @author Andreas Fischer <bantu@owncloud.com> * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Birk Borkason <daniel.niccoli@gmail.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Brice Maron <brice@bmaron.net> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Christoph Wurst <christoph@owncloud.com> * @author Clark Tomlinson <fallen013@gmail.com> * @author cmeh <cmeh@users.noreply.github.com> * @author Felix Anand Epp <work@felixepp.de> * @author Florin Peter <github@florin-peter.de> * @author Frank Karlitschek <frank@karlitschek.de> * @author Georg Ehrke <georg@owncloud.com> * @author helix84 <helix84@centrum.sk> * @author Individual IT Services <info@individual-it.net> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Markus Goetz <markus@woboq.com> * @author Martin Mattel <martin.mattel@diemattels.at> * @author Marvin Thomas Rabe <mrabe@marvinrabe.de> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Rado <owncloud@sradonia.net> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * @author Volkan Gezer <volkangezer@gmail.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OCP\IConfig; use OCP\IGroupManager; use OCP\IUser; class OC_Util { public static $scripts = array(); public static $styles = array(); public static $headers = array(); private static $rootMounted = false; private static $fsSetup = false; /** @var array Local cache of version.php */ private static $versionCache = null; protected static function getAppManager() { return \OC::$server->getAppManager(); } private static function initLocalStorageRootFS() { // mount local file backend as root $configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data"); //first set up the local "root" storage \OC\Files\Filesystem::initMountManager(); if (!self::$rootMounted) { \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/'); self::$rootMounted = true; } } /** * mounting an object storage as the root fs will in essence remove the * necessity of a data folder being present. * TODO make home storage aware of this and use the object storage instead of local disk access * * @param array $config containing 'class' and optional 'arguments' */ private static function initObjectStoreRootFS($config) { // check misconfiguration if (empty($config['class'])) { \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR); } if (!isset($config['arguments'])) { $config['arguments'] = array(); } // instantiate object store implementation $name = $config['class']; if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) { $segments = explode('\\', $name); OC_App::loadApp(strtolower($segments[1])); } $config['arguments']['objectstore'] = new $config['class']($config['arguments']); // mount with plain / root object store implementation $config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage'; // mount object storage as root \OC\Files\Filesystem::initMountManager(); if (!self::$rootMounted) { \OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/'); self::$rootMounted = true; } } /** * mounting an object storage as the root fs will in essence remove the * necessity of a data folder being present. * * @param array $config containing 'class' and optional 'arguments' */ private static function initObjectStoreMultibucketRootFS($config) { // check misconfiguration if (empty($config['class'])) { \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR); } if (!isset($config['arguments'])) { $config['arguments'] = array(); } // instantiate object store implementation $name = $config['class']; if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) { $segments = explode('\\', $name); OC_App::loadApp(strtolower($segments[1])); } if (!isset($config['arguments']['bucket'])) { $config['arguments']['bucket'] = ''; } // put the root FS always in first bucket for multibucket configuration $config['arguments']['bucket'] .= '0'; $config['arguments']['objectstore'] = new $config['class']($config['arguments']); // mount with plain / root object store implementation $config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage'; // mount object storage as root \OC\Files\Filesystem::initMountManager(); if (!self::$rootMounted) { \OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/'); self::$rootMounted = true; } } /** * Can be set up * * @param string $user * @return boolean * @description configure the initial filesystem based on the configuration */ public static function setupFS($user = '') { //setting up the filesystem twice can only lead to trouble if (self::$fsSetup) { return false; } \OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem'); // If we are not forced to load a specific user we load the one that is logged in if ($user === null) { $user = ''; } else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) { $user = OC_User::getUser(); } // load all filesystem apps before, so no setup-hook gets lost OC_App::loadApps(array('filesystem')); // the filesystem will finish when $user is not empty, // mark fs setup here to avoid doing the setup from loading // OC_Filesystem if ($user != '') { self::$fsSetup = true; } \OC\Files\Filesystem::initMountManager(); \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false); \OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) { if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) { /** @var \OC\Files\Storage\Common $storage */ $storage->setMountOptions($mount->getOptions()); } return $storage; }); \OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) { if (!$mount->getOption('enable_sharing', true)) { return new \OC\Files\Storage\Wrapper\PermissionsMask([ 'storage' => $storage, 'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE ]); } return $storage; }); // install storage availability wrapper, before most other wrappers \OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) { if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) { return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]); } return $storage; }); \OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) { if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) { return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]); } return $storage; }); \OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) { // set up quota for home storages, even for other users // which can happen when using sharing /** * @var \OC\Files\Storage\Storage $storage */ if ($storage->instanceOfStorage('\OC\Files\Storage\Home') || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage') ) { /** @var \OC\Files\Storage\Home $storage */ if (is_object($storage->getUser())) { $user = $storage->getUser()->getUID(); $quota = OC_Util::getUserQuota($user); if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) { return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files')); } } } return $storage; }); OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user)); \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true); //check if we are using an object storage $objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null); $objectStoreMultibucket = \OC::$server->getSystemConfig()->getValue('objectstore_multibucket', null); // use the same order as in ObjectHomeMountProvider if (isset($objectStoreMultibucket)) { self::initObjectStoreMultibucketRootFS($objectStoreMultibucket); } elseif (isset($objectStore)) { self::initObjectStoreRootFS($objectStore); } else { self::initLocalStorageRootFS(); } if ($user != '' && !OCP\User::userExists($user)) { \OC::$server->getEventLogger()->end('setup_fs'); return false; } //if we aren't logged in, there is no use to set up the filesystem if ($user != "") { $userDir = '/' . $user . '/files'; //jail the user into his "home" directory \OC\Files\Filesystem::init($user, $userDir); OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir)); } \OC::$server->getEventLogger()->end('setup_fs'); return true; } /** * check if a password is required for each public link * * @return boolean */ public static function isPublicLinkPasswordRequired() { $appConfig = \OC::$server->getAppConfig(); $enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password', 'no'); return ($enforcePassword === 'yes') ? true : false; } /** * check if sharing is disabled for the current user * @param IConfig $config * @param IGroupManager $groupManager * @param IUser|null $user * @return bool */ public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) { if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { $groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', ''); $excludedGroups = json_decode($groupsList); if (is_null($excludedGroups)) { $excludedGroups = explode(',', $groupsList); $newValue = json_encode($excludedGroups); $config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); } $usersGroups = $groupManager->getUserGroupIds($user); if (!empty($usersGroups)) { $remainingGroups = array_diff($usersGroups, $excludedGroups); // if the user is only in groups which are disabled for sharing then // sharing is also disabled for the user if (empty($remainingGroups)) { return true; } } } return false; } /** * check if share API enforces a default expire date * * @return boolean */ public static function isDefaultExpireDateEnforced() { $isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no'); $enforceDefaultExpireDate = false; if ($isDefaultExpireDateEnabled === 'yes') { $value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no'); $enforceDefaultExpireDate = ($value === 'yes') ? true : false; } return $enforceDefaultExpireDate; } /** * Get the quota of a user * * @param string $userId * @return int Quota bytes */ public static function getUserQuota($userId) { $user = \OC::$server->getUserManager()->get($userId); if (is_null($user)) { return \OCP\Files\FileInfo::SPACE_UNLIMITED; } $userQuota = $user->getQuota(); if($userQuota === 'none') { return \OCP\Files\FileInfo::SPACE_UNLIMITED; } return OC_Helper::computerFileSize($userQuota); } /** * copies the skeleton to the users /files * * @param String $userId * @param \OCP\Files\Folder $userDirectory * @throws \RuntimeException */ public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) { $skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton'); $instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', ''); if ($instanceId === null) { throw new \RuntimeException('no instance id!'); } $appdata = 'appdata_' . $instanceId; if ($userId === $appdata) { throw new \RuntimeException('username is reserved name: ' . $appdata); } if (!empty($skeletonDirectory)) { \OCP\Util::writeLog( 'files_skeleton', 'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'), \OCP\Util::DEBUG ); self::copyr($skeletonDirectory, $userDirectory); // update the file cache $userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE); } } /** * copies a directory recursively by using streams * * @param string $source * @param \OCP\Files\Folder $target * @return void */ public static function copyr($source, \OCP\Files\Folder $target) { $logger = \OC::$server->getLogger(); // Verify if folder exists $dir = opendir($source); if($dir === false) { $logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']); return; } // Copy the files while (false !== ($file = readdir($dir))) { if (!\OC\Files\Filesystem::isIgnoredDir($file)) { if (is_dir($source . '/' . $file)) { $child = $target->newFolder($file); self::copyr($source . '/' . $file, $child); } else { $child = $target->newFile($file); $sourceStream = fopen($source . '/' . $file, 'r'); if($sourceStream === false) { $logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']); closedir($dir); return; } stream_copy_to_stream($sourceStream, $child->fopen('w')); } } } closedir($dir); } /** * @return void */ public static function tearDownFS() { \OC\Files\Filesystem::tearDown(); \OC::$server->getRootFolder()->clearCache(); self::$fsSetup = false; self::$rootMounted = false; } /** * get the current installed version of ownCloud * * @return array */ public static function getVersion() { OC_Util::loadVersion(); return self::$versionCache['OC_Version']; } /** * get the current installed version string of ownCloud * * @return string */ public static function getVersionString() { OC_Util::loadVersion(); return self::$versionCache['OC_VersionString']; } /** * @deprecated the value is of no use anymore * @return string */ public static function getEditionString() { return ''; } /** * @description get the update channel of the current installed of ownCloud. * @return string */ public static function getChannel() { OC_Util::loadVersion(); return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']); } /** * @description get the build number of the current installed of ownCloud. * @return string */ public static function getBuild() { OC_Util::loadVersion(); return self::$versionCache['OC_Build']; } /** * @description load the version.php into the session as cache */ private static function loadVersion() { if (self::$versionCache !== null) { return; } $timestamp = filemtime(OC::$SERVERROOT . '/version.php'); require OC::$SERVERROOT . '/version.php'; /** @var $timestamp int */ self::$versionCache['OC_Version_Timestamp'] = $timestamp; /** @var $OC_Version string */ self::$versionCache['OC_Version'] = $OC_Version; /** @var $OC_VersionString string */ self::$versionCache['OC_VersionString'] = $OC_VersionString; /** @var $OC_Build string */ self::$versionCache['OC_Build'] = $OC_Build; /** @var $OC_Channel string */ self::$versionCache['OC_Channel'] = $OC_Channel; } /** * generates a path for JS/CSS files. If no application is provided it will create the path for core. * * @param string $application application to get the files from * @param string $directory directory within this application (css, js, vendor, etc) * @param string $file the file inside of the above folder * @return string the path */ private static function generatePath($application, $directory, $file) { if (is_null($file)) { $file = $application; $application = ""; } if (!empty($application)) { return "$application/$directory/$file"; } else { return "$directory/$file"; } } /** * add a javascript file * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Script to the beginning of the list * @return void */ public static function addScript($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'js', $file); // core js files need separate handling if ($application !== 'core' && $file !== null) { self::addTranslations ( $application ); } self::addExternalResource($application, $prepend, $path, "script"); } /** * add a javascript file from the vendor sub folder * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Script to the beginning of the list * @return void */ public static function addVendorScript($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'vendor', $file); self::addExternalResource($application, $prepend, $path, "script"); } /** * add a translation JS file * * @param string $application application id * @param string $languageCode language code, defaults to the current language * @param bool $prepend prepend the Script to the beginning of the list */ public static function addTranslations($application, $languageCode = null, $prepend = false) { if (is_null($languageCode)) { $languageCode = \OC::$server->getL10NFactory()->findLanguage($application); } if (!empty($application)) { $path = "$application/l10n/$languageCode"; } else { $path = "l10n/$languageCode"; } self::addExternalResource($application, $prepend, $path, "script"); } /** * add a css file * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Style to the beginning of the list * @return void */ public static function addStyle($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'css', $file); self::addExternalResource($application, $prepend, $path, "style"); } /** * add a css file from the vendor sub folder * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Style to the beginning of the list * @return void */ public static function addVendorStyle($application, $file = null, $prepend = false) { $path = OC_Util::generatePath($application, 'vendor', $file); self::addExternalResource($application, $prepend, $path, "style"); } /** * add an external resource css/js file * * @param string $application application id * @param bool $prepend prepend the file to the beginning of the list * @param string $path * @param string $type (script or style) * @return void */ private static function addExternalResource($application, $prepend, $path, $type = "script") { if ($type === "style") { if (!in_array($path, self::$styles)) { if ($prepend === true) { array_unshift ( self::$styles, $path ); } else { self::$styles[] = $path; } } } elseif ($type === "script") { if (!in_array($path, self::$scripts)) { if ($prepend === true) { array_unshift ( self::$scripts, $path ); } else { self::$scripts [] = $path; } } } } /** * Add a custom element to the header * If $text is null then the element will be written as empty element. * So use "" to get a closing tag. * @param string $tag tag name of the element * @param array $attributes array of attributes for the element * @param string $text the text content for the element */ public static function addHeader($tag, $attributes, $text=null) { self::$headers[] = array( 'tag' => $tag, 'attributes' => $attributes, 'text' => $text ); } /** * formats a timestamp in the "right" way * * @param int $timestamp * @param bool $dateOnly option to omit time from the result * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to * @return string timestamp * * @deprecated Use \OC::$server->query('DateTimeFormatter') instead */ public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) { if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) { $timeZone = new \DateTimeZone($timeZone); } /** @var \OC\DateTimeFormatter $formatter */ $formatter = \OC::$server->query('DateTimeFormatter'); if ($dateOnly) { return $formatter->formatDate($timestamp, 'long', $timeZone); } return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone); } /** * check if the current server configuration is suitable for ownCloud * * @param \OC\SystemConfig $config * @return array arrays with error messages and hints */ public static function checkServer(\OC\SystemConfig $config) { $l = \OC::$server->getL10N('lib'); $errors = array(); $CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data'); if (!self::needUpgrade($config) && $config->getValue('installed', false)) { // this check needs to be done every time $errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY); } // Assume that if checkServer() succeeded before in this session, then all is fine. if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) { return $errors; } $webServerRestart = false; $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), \OC::$server->query(\OCP\Defaults::class), \OC::$server->getLogger(), \OC::$server->getSecureRandom()); $urlGenerator = \OC::$server->getURLGenerator(); $availableDatabases = $setup->getSupportedDatabases(); if (empty($availableDatabases)) { $errors[] = array( 'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'), 'hint' => '' //TODO: sane hint ); $webServerRestart = true; } // Check if config folder is writable. if(!OC_Helper::isReadOnlyConfigEnabled()) { if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) { $errors[] = array( 'error' => $l->t('Cannot write into "config" directory'), 'hint' => $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s', [$urlGenerator->linkToDocs('admin-dir_permissions')]) ); } } // Check if there is a writable install folder. if ($config->getValue('appstoreenabled', true)) { if (OC_App::getInstallPath() === null || !is_writable(OC_App::getInstallPath()) || !is_readable(OC_App::getInstallPath()) ) { $errors[] = array( 'error' => $l->t('Cannot write into "apps" directory'), 'hint' => $l->t('This can usually be fixed by giving the webserver write access to the apps directory' . ' or disabling the appstore in the config file. See %s', [$urlGenerator->linkToDocs('admin-dir_permissions')]) ); } } // Create root dir. if ($config->getValue('installed', false)) { if (!is_dir($CONFIG_DATADIRECTORY)) { $success = @mkdir($CONFIG_DATADIRECTORY); if ($success) { $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY)); } else { $errors[] = [ 'error' => $l->t('Cannot create "data" directory'), 'hint' => $l->t('This can usually be fixed by giving the webserver write access to the root directory. See %s', [$urlGenerator->linkToDocs('admin-dir_permissions')]) ]; } } else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) { //common hint for all file permissions error messages $permissionsHint = $l->t('Permissions can usually be fixed by giving the webserver write access to the root directory. See %s.', [$urlGenerator->linkToDocs('admin-dir_permissions')]); $errors[] = [ 'error' => 'Your data directory is not writable', 'hint' => $permissionsHint ]; } else { $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY)); } } if (!OC_Util::isSetLocaleWorking()) { $errors[] = array( 'error' => $l->t('Setting locale to %s failed', array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/' . 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')), 'hint' => $l->t('Please install one of these locales on your system and restart your webserver.') ); } // Contains the dependencies that should be checked against // classes = class_exists // functions = function_exists // defined = defined // ini = ini_get // If the dependency is not found the missing module name is shown to the EndUser // When adding new checks always verify that they pass on Travis as well // for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini $dependencies = array( 'classes' => array( 'ZipArchive' => 'zip', 'DOMDocument' => 'dom', 'XMLWriter' => 'XMLWriter', 'XMLReader' => 'XMLReader', ), 'functions' => [ 'xml_parser_create' => 'libxml', 'mb_strcut' => 'mb multibyte', 'ctype_digit' => 'ctype', 'json_encode' => 'JSON', 'gd_info' => 'GD', 'gzencode' => 'zlib', 'iconv' => 'iconv', 'simplexml_load_string' => 'SimpleXML', 'hash' => 'HASH Message Digest Framework', 'curl_init' => 'cURL', 'openssl_verify' => 'OpenSSL', ], 'defined' => array( 'PDO::ATTR_DRIVER_NAME' => 'PDO' ), 'ini' => [ 'default_charset' => 'UTF-8', ], ); $missingDependencies = array(); $invalidIniSettings = []; $moduleHint = $l->t('Please ask your server administrator to install the module.'); /** * FIXME: The dependency check does not work properly on HHVM on the moment * and prevents installation. Once HHVM is more compatible with our * approach to check for these values we should re-enable those * checks. */ $iniWrapper = \OC::$server->getIniWrapper(); if (!self::runningOnHhvm()) { foreach ($dependencies['classes'] as $class => $module) { if (!class_exists($class)) { $missingDependencies[] = $module; } } foreach ($dependencies['functions'] as $function => $module) { if (!function_exists($function)) { $missingDependencies[] = $module; } } foreach ($dependencies['defined'] as $defined => $module) { if (!defined($defined)) { $missingDependencies[] = $module; } } foreach ($dependencies['ini'] as $setting => $expected) { if (is_bool($expected)) { if ($iniWrapper->getBool($setting) !== $expected) { $invalidIniSettings[] = [$setting, $expected]; } } if (is_int($expected)) { if ($iniWrapper->getNumeric($setting) !== $expected) { $invalidIniSettings[] = [$setting, $expected]; } } if (is_string($expected)) { if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) { $invalidIniSettings[] = [$setting, $expected]; } } } } foreach($missingDependencies as $missingDependency) { $errors[] = array( 'error' => $l->t('PHP module %s not installed.', array($missingDependency)), 'hint' => $moduleHint ); $webServerRestart = true; } foreach($invalidIniSettings as $setting) { if(is_bool($setting[1])) { $setting[1] = ($setting[1]) ? 'on' : 'off'; } $errors[] = [ 'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]), 'hint' => $l->t('Adjusting this setting in php.ini will make Nextcloud run again') ]; $webServerRestart = true; } /** * The mbstring.func_overload check can only be performed if the mbstring * module is installed as it will return null if the checking setting is * not available and thus a check on the boolean value fails. * * TODO: Should probably be implemented in the above generic dependency * check somehow in the long-term. */ if($iniWrapper->getBool('mbstring.func_overload') !== null && $iniWrapper->getBool('mbstring.func_overload') === true) { $errors[] = array( 'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]), 'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini') ); } if(function_exists('xml_parser_create') && LIBXML_LOADED_VERSION < 20700 ) { $version = LIBXML_LOADED_VERSION; $major = floor($version/10000); $version -= ($major * 10000); $minor = floor($version/100); $version -= ($minor * 100); $patch = $version; $errors[] = array( 'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]), 'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.') ); } if (!self::isAnnotationsWorking()) { $errors[] = array( 'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'), 'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.') ); } if (!\OC::$CLI && $webServerRestart) { $errors[] = array( 'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'), 'hint' => $l->t('Please ask your server administrator to restart the web server.') ); } $errors = array_merge($errors, self::checkDatabaseVersion()); // Cache the result of this function \OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0); return $errors; } /** * Check the database version * * @return array errors array */ public static function checkDatabaseVersion() { $l = \OC::$server->getL10N('lib'); $errors = array(); $dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite'); if ($dbType === 'pgsql') { // check PostgreSQL version try { $result = \OC_DB::executeAudited('SHOW SERVER_VERSION'); $data = $result->fetchRow(); if (isset($data['server_version'])) { $version = $data['server_version']; if (version_compare($version, '9.0.0', '<')) { $errors[] = array( 'error' => $l->t('PostgreSQL >= 9 required'), 'hint' => $l->t('Please upgrade your database version') ); } } } catch (\Doctrine\DBAL\DBALException $e) { $logger = \OC::$server->getLogger(); $logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9'); $logger->logException($e); } } return $errors; } /** * Check for correct file permissions of data directory * * @param string $dataDirectory * @return array arrays with error messages and hints */ public static function checkDataDirectoryPermissions($dataDirectory) { $l = \OC::$server->getL10N('lib'); $errors = array(); $permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory' . ' cannot be listed by other users.'); $perms = substr(decoct(@fileperms($dataDirectory)), -3); if (substr($perms, -1) !== '0') { chmod($dataDirectory, 0770); clearstatcache(); $perms = substr(decoct(@fileperms($dataDirectory)), -3); if ($perms[2] !== '0') { $errors[] = [ 'error' => $l->t('Your data directory is readable by other users'), 'hint' => $permissionsModHint ]; } } return $errors; } /** * Check that the data directory exists and is valid by * checking the existence of the ".ocdata" file. * * @param string $dataDirectory data directory path * @return array errors found */ public static function checkDataDirectoryValidity($dataDirectory) { $l = \OC::$server->getL10N('lib'); $errors = []; if ($dataDirectory[0] !== '/') { $errors[] = [ 'error' => $l->t('Your data directory must be an absolute path'), 'hint' => $l->t('Check the value of "datadirectory" in your configuration') ]; } if (!file_exists($dataDirectory . '/.ocdata')) { $errors[] = [ 'error' => $l->t('Your data directory is invalid'), 'hint' => $l->t('Ensure there is a file called ".ocdata"' . ' in the root of the data directory.') ]; } return $errors; } /** * Check if the user is logged in, redirects to home if not. With * redirect URL parameter to the request URI. * * @return void */ public static function checkLoggedIn() { // Check if we are a user if (!\OC::$server->getUserSession()->isLoggedIn()) { header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute( 'core.login.showLoginForm', [ 'redirect_url' => \OC::$server->getRequest()->getRequestUri(), ] ) ); exit(); } // Redirect to 2FA challenge selection if 2FA challenge was not solved yet if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.selectChallenge')); exit(); } } /** * Check if the user is a admin, redirects to home if not * * @return void */ public static function checkAdminUser() { OC_Util::checkLoggedIn(); if (!OC_User::isAdminUser(OC_User::getUser())) { header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php')); exit(); } } /** * Check if the user is a subadmin, redirects to home if not * * @return null|boolean $groups where the current user is subadmin */ public static function checkSubAdminUser() { OC_Util::checkLoggedIn(); $userObject = \OC::$server->getUserSession()->getUser(); $isSubAdmin = false; if($userObject !== null) { $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject); } if (!$isSubAdmin) { header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php')); exit(); } return true; } /** * Returns the URL of the default page * based on the system configuration and * the apps visible for the current user * * @return string URL */ public static function getDefaultPageUrl() { $urlGenerator = \OC::$server->getURLGenerator(); // Deny the redirect if the URL contains a @ // This prevents unvalidated redirects like ?redirect_url=:user@domain.com if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) { $location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url'])); } else { $defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage'); if ($defaultPage) { $location = $urlGenerator->getAbsoluteURL($defaultPage); } else { $appId = 'files'; $defaultApps = explode(',', \OCP\Config::getSystemValue('defaultapp', 'files')); // find the first app that is enabled for the current user foreach ($defaultApps as $defaultApp) { $defaultApp = OC_App::cleanAppId(strip_tags($defaultApp)); if (static::getAppManager()->isEnabledForUser($defaultApp)) { $appId = $defaultApp; break; } } if(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') { $location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/'); } else { $location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/'); } } } return $location; } /** * Redirect to the user default page * * @return void */ public static function redirectToDefaultPage() { $location = self::getDefaultPageUrl(); header('Location: ' . $location); exit(); } /** * get an id unique for this instance * * @return string */ public static function getInstanceId() { $id = \OC::$server->getSystemConfig()->getValue('instanceid', null); if (is_null($id)) { // We need to guarantee at least one letter in instanceid so it can be used as the session_name $id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS); \OC::$server->getSystemConfig()->setValue('instanceid', $id); } return $id; } /** * Public function to sanitize HTML * * This function is used to sanitize HTML and should be applied on any * string or array of strings before displaying it on a web page. * * @param string|array $value * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter. */ public static function sanitizeHTML($value) { if (is_array($value)) { $value = array_map(function($value) { return self::sanitizeHTML($value); }, $value); } else { // Specify encoding for PHP<5.4 $value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); } return $value; } /** * Public function to encode url parameters * * This function is used to encode path to file before output. * Encoding is done according to RFC 3986 with one exception: * Character '/' is preserved as is. * * @param string $component part of URI to encode * @return string */ public static function encodePath($component) { $encoded = rawurlencode($component); $encoded = str_replace('%2F', '/', $encoded); return $encoded; } public function createHtaccessTestFile(\OCP\IConfig $config) { // php dev server does not support htaccess if (php_sapi_name() === 'cli-server') { return false; } // testdata $fileName = '/htaccesstest.txt'; $testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.'; // creating a test file $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName; if (file_exists($testFile)) {// already running this test, possible recursive call return false; } $fp = @fopen($testFile, 'w'); if (!$fp) { throw new OC\HintException('Can\'t create test file to check for working .htaccess file.', 'Make sure it is possible for the webserver to write to ' . $testFile); } fwrite($fp, $testContent); fclose($fp); return $testContent; } /** * Check if the .htaccess file is working * @param \OCP\IConfig $config * @return bool * @throws Exception * @throws \OC\HintException If the test file can't get written. */ public function isHtaccessWorking(\OCP\IConfig $config) { if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) { return true; } $testContent = $this->createHtaccessTestFile($config); if ($testContent === false) { return false; } $fileName = '/htaccesstest.txt'; $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName; // accessing the file via http $url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName); try { $content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody(); } catch (\Exception $e) { $content = false; } // cleanup @unlink($testFile); /* * If the content is not equal to test content our .htaccess * is working as required */ return $content !== $testContent; } /** * Check if the setlocal call does not work. This can happen if the right * local packages are not available on the server. * * @return bool */ public static function isSetLocaleWorking() { \Patchwork\Utf8\Bootup::initLocale(); if ('' === basename('§')) { return false; } return true; } /** * Check if it's possible to get the inline annotations * * @return bool */ public static function isAnnotationsWorking() { $reflection = new \ReflectionMethod(__METHOD__); $docs = $reflection->getDocComment(); return (is_string($docs) && strlen($docs) > 50); } /** * Check if the PHP module fileinfo is loaded. * * @return bool */ public static function fileInfoLoaded() { return function_exists('finfo_open'); } /** * clear all levels of output buffering * * @return void */ public static function obEnd() { while (ob_get_level()) { ob_end_clean(); } } /** * Checks whether the server is running on Mac OS X * * @return bool true if running on Mac OS X, false otherwise */ public static function runningOnMac() { return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN'); } /** * Checks whether server is running on HHVM * * @return bool True if running on HHVM, false otherwise */ public static function runningOnHhvm() { return defined('HHVM_VERSION'); } /** * Handles the case that there may not be a theme, then check if a "default" * theme exists and take that one * * @return string the theme */ public static function getTheme() { $theme = \OC::$server->getSystemConfig()->getValue("theme", ''); if ($theme === '') { if (is_dir(OC::$SERVERROOT . '/themes/default')) { $theme = 'default'; } } return $theme; } /** * Clear a single file from the opcode cache * This is useful for writing to the config file * in case the opcode cache does not re-validate files * Returns true if successful, false if unsuccessful: * caller should fall back on clearing the entire cache * with clearOpcodeCache() if unsuccessful * * @param string $path the path of the file to clear from the cache * @return bool true if underlying function returns true, otherwise false */ public static function deleteFromOpcodeCache($path) { $ret = false; if ($path) { // APC >= 3.1.1 if (function_exists('apc_delete_file')) { $ret = @apc_delete_file($path); } // Zend OpCache >= 7.0.0, PHP >= 5.5.0 if (function_exists('opcache_invalidate')) { $ret = opcache_invalidate($path); } } return $ret; } /** * Clear the opcode cache if one exists * This is necessary for writing to the config file * in case the opcode cache does not re-validate files * * @return void */ public static function clearOpcodeCache() { // APC if (function_exists('apc_clear_cache')) { apc_clear_cache(); } // Zend Opcache if (function_exists('accelerator_reset')) { accelerator_reset(); } // XCache if (function_exists('xcache_clear_cache')) { if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) { \OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN); } else { @xcache_clear_cache(XC_TYPE_PHP, 0); } } // Opcache (PHP >= 5.5) if (function_exists('opcache_reset')) { opcache_reset(); } } /** * Normalize a unicode string * * @param string $value a not normalized string * @return bool|string */ public static function normalizeUnicode($value) { if(Normalizer::isNormalized($value)) { return $value; } $normalizedValue = Normalizer::normalize($value); if ($normalizedValue === null || $normalizedValue === false) { \OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']); return $value; } return $normalizedValue; } /** * @param boolean|string $file * @return string */ public static function basename($file) { $file = rtrim($file, '/'); $t = explode('/', $file); return array_pop($t); } /** * A human readable string is generated based on version and build number * * @return string */ public static function getHumanVersion() { $version = OC_Util::getVersionString(); $build = OC_Util::getBuild(); if (!empty($build) and OC_Util::getChannel() === 'daily') { $version .= ' Build:' . $build; } return $version; } /** * Returns whether the given file name is valid * * @param string $file file name to check * @return bool true if the file name is valid, false otherwise * @deprecated use \OC\Files\View::verifyPath() */ public static function isValidFileName($file) { $trimmed = trim($file); if ($trimmed === '') { return false; } if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) { return false; } // detect part files if (preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) { return false; } foreach (str_split($trimmed) as $char) { if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) { return false; } } return true; } /** * Check whether the instance needs to perform an upgrade, * either when the core version is higher or any app requires * an upgrade. * * @param \OC\SystemConfig $config * @return bool whether the core or any app needs an upgrade * @throws \OC\HintException When the upgrade from the given version is not allowed */ public static function needUpgrade(\OC\SystemConfig $config) { if ($config->getValue('installed', false)) { $installedVersion = $config->getValue('version', '0.0.0'); $currentVersion = implode('.', \OCP\Util::getVersion()); $versionDiff = version_compare($currentVersion, $installedVersion); if ($versionDiff > 0) { return true; } else if ($config->getValue('debug', false) && $versionDiff < 0) { // downgrade with debug $installedMajor = explode('.', $installedVersion); $installedMajor = $installedMajor[0] . '.' . $installedMajor[1]; $currentMajor = explode('.', $currentVersion); $currentMajor = $currentMajor[0] . '.' . $currentMajor[1]; if ($installedMajor === $currentMajor) { // Same major, allow downgrade for developers return true; } else { // downgrade attempt, throw exception throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')'); } } else if ($versionDiff < 0) { // downgrade attempt, throw exception throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')'); } // also check for upgrades for apps (independently from the user) $apps = \OC_App::getEnabledApps(false, true); $shouldUpgrade = false; foreach ($apps as $app) { if (\OC_App::shouldUpgrade($app)) { $shouldUpgrade = true; break; } } return $shouldUpgrade; } else { return false; } } } private/legacy/json.php 0000604 00000013533 15247130452 0011150 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Christoph Wurst <christoph@owncloud.com> * @author Felix Moeller <mail@felixmoeller.de> * @author Georg Ehrke <georg@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Class OC_JSON * @deprecated Use a AppFramework JSONResponse instead */ class OC_JSON{ static protected $send_content_type_header = false; /** * set Content-Type header to jsonrequest * @deprecated Use a AppFramework JSONResponse instead */ public static function setContentTypeHeader($type='application/json') { if (!self::$send_content_type_header) { // We send json data header( 'Content-Type: '.$type . '; charset=utf-8'); self::$send_content_type_header = true; } } /** * Check if the app is enabled, send json error msg if not * @param string $app * @deprecated Use the AppFramework instead. It will automatically check if the app is enabled. */ public static function checkAppEnabled($app) { if( !OC_App::isEnabled($app)) { $l = \OC::$server->getL10N('lib'); self::error(array( 'data' => array( 'message' => $l->t('Application is not enabled'), 'error' => 'application_not_enabled' ))); exit(); } } /** * Check if the user is logged in, send json error msg if not * @deprecated Use annotation based ACLs from the AppFramework instead */ public static function checkLoggedIn() { $twoFactorAuthManger = \OC::$server->getTwoFactorAuthManager(); if( !\OC::$server->getUserSession()->isLoggedIn() || $twoFactorAuthManger->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { $l = \OC::$server->getL10N('lib'); http_response_code(\OCP\AppFramework\Http::STATUS_UNAUTHORIZED); self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' ))); exit(); } } /** * Check an ajax get/post call if the request token is valid, send json error msg if not. * @deprecated Use annotation based CSRF checks from the AppFramework instead */ public static function callCheck() { if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { header('Location: '.\OC::$WEBROOT); exit(); } if( !(\OC::$server->getRequest()->passesCSRFCheck())) { $l = \OC::$server->getL10N('lib'); self::error(array( 'data' => array( 'message' => $l->t('Token expired. Please reload page.'), 'error' => 'token_expired' ))); exit(); } } /** * Check if the user is a admin, send json error msg if not. * @deprecated Use annotation based ACLs from the AppFramework instead */ public static function checkAdminUser() { if( !OC_User::isAdminUser(OC_User::getUser())) { $l = \OC::$server->getL10N('lib'); self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' ))); exit(); } } /** * Check is a given user exists - send json error msg if not * @param string $user * @deprecated Use a AppFramework JSONResponse instead */ public static function checkUserExists($user) { if (!OCP\User::userExists($user)) { $l = \OC::$server->getL10N('lib'); OCP\JSON::error(array('data' => array('message' => $l->t('Unknown user'), 'error' => 'unknown_user' ))); exit; } } /** * Check if the user is a subadmin, send json error msg if not * @deprecated Use annotation based ACLs from the AppFramework instead */ public static function checkSubAdminUser() { $userObject = \OC::$server->getUserSession()->getUser(); $isSubAdmin = false; if($userObject !== null) { $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject); } if(!$isSubAdmin) { $l = \OC::$server->getL10N('lib'); self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' ))); exit(); } } /** * Send json error msg * @deprecated Use a AppFramework JSONResponse instead */ public static function error($data = array()) { $data['status'] = 'error'; self::encodedPrint($data); } /** * Send json success msg * @deprecated Use a AppFramework JSONResponse instead */ public static function success($data = array()) { $data['status'] = 'success'; self::encodedPrint($data); } /** * Convert OC_L10N_String to string, for use in json encodings */ protected static function to_string(&$value) { if ($value instanceof OC_L10N_String) { $value = (string)$value; } } /** * Encode and print $data in json format * @deprecated Use a AppFramework JSONResponse instead */ public static function encodedPrint($data, $setContentType=true) { if($setContentType) { self::setContentTypeHeader(); } echo self::encode($data); } /** * Encode JSON * @deprecated Use a AppFramework JSONResponse instead */ public static function encode($data) { if (is_array($data)) { array_walk_recursive($data, array('OC_JSON', 'to_string')); } return json_encode($data, JSON_HEX_TAG); } } private/legacy/l10n/string.php 0000604 00000004227 15247130452 0012257 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_L10N_String implements JsonSerializable { /** @var \OC\L10N\L10N */ protected $l10n; /** @var string */ protected $text; /** @var array */ protected $parameters; /** @var integer */ protected $count; /** * @param \OC\L10N\L10N $l10n * @param string|string[] $text * @param array $parameters * @param int $count */ public function __construct(\OC\L10N\L10N $l10n, $text, $parameters, $count = 1) { $this->l10n = $l10n; $this->text = $text; $this->parameters = $parameters; $this->count = $count; } public function __toString() { $translations = $this->l10n->getTranslations(); $text = $this->text; if(array_key_exists($this->text, $translations)) { if(is_array($translations[$this->text])) { $fn = $this->l10n->getPluralFormFunction(); $id = $fn($this->count); $text = $translations[$this->text][$id]; } else{ $text = $translations[$this->text]; } } // Replace %n first (won't interfere with vsprintf) $text = str_replace('%n', $this->count, $text); return vsprintf($text, $this->parameters); } public function jsonSerialize() { return $this->__toString(); } } private/legacy/hook.php 0000604 00000010366 15247130452 0011140 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Sam Tuke <mail@samtuke.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_Hook{ public static $thrownExceptions = []; static private $registered = array(); /** * connects a function to a hook * * @param string $signalClass class name of emitter * @param string $signalName name of signal * @param string|object $slotClass class name of slot * @param string $slotName name of slot * @return bool * * This function makes it very easy to connect to use hooks. * * TODO: write example */ static public function connect($signalClass, $signalName, $slotClass, $slotName ) { // If we're trying to connect to an emitting class that isn't // yet registered, register it if( !array_key_exists($signalClass, self::$registered )) { self::$registered[$signalClass] = array(); } // If we're trying to connect to an emitting method that isn't // yet registered, register it with the emitting class if( !array_key_exists( $signalName, self::$registered[$signalClass] )) { self::$registered[$signalClass][$signalName] = array(); } // don't connect hooks twice foreach (self::$registered[$signalClass][$signalName] as $hook) { if ($hook['class'] === $slotClass and $hook['name'] === $slotName) { return false; } } // Connect the hook handler to the requested emitter self::$registered[$signalClass][$signalName][] = array( "class" => $slotClass, "name" => $slotName ); // No chance for failure ;-) return true; } /** * emits a signal * * @param string $signalClass class name of emitter * @param string $signalName name of signal * @param mixed $params default: array() array with additional data * @return bool true if slots exists or false if not * @throws \OC\HintException * @throws \OC\ServerNotAvailableException Emits a signal. To get data from the slot use references! * * TODO: write example */ static public function emit($signalClass, $signalName, $params = []) { // Return false if no hook handlers are listening to this // emitting class if( !array_key_exists($signalClass, self::$registered )) { return false; } // Return false if no hook handlers are listening to this // emitting method if( !array_key_exists( $signalName, self::$registered[$signalClass] )) { return false; } // Call all slots foreach( self::$registered[$signalClass][$signalName] as $i ) { try { call_user_func( array( $i["class"], $i["name"] ), $params ); } catch (Exception $e){ self::$thrownExceptions[] = $e; \OC::$server->getLogger()->logException($e); if($e instanceof \OC\HintException) { throw $e; } if($e instanceof \OC\ServerNotAvailableException) { throw $e; } } } return true; } /** * clear hooks * @param string $signalClass * @param string $signalName */ static public function clear($signalClass='', $signalName='') { if ($signalClass) { if ($signalName) { self::$registered[$signalClass][$signalName]=array(); }else{ self::$registered[$signalClass]=array(); } }else{ self::$registered=array(); } } /** * DO NOT USE! * For unit tests ONLY! */ static public function getHooks() { return self::$registered; } } private/legacy/api.php 0000604 00000034115 15247130452 0010747 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Christoph Wurst <christoph@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OCP\API; use OCP\AppFramework\Http; class OC_API { /** * API authentication levels */ /** @deprecated Use \OCP\API::GUEST_AUTH instead */ const GUEST_AUTH = 0; /** @deprecated Use \OCP\API::USER_AUTH instead */ const USER_AUTH = 1; /** @deprecated Use \OCP\API::SUBADMIN_AUTH instead */ const SUBADMIN_AUTH = 2; /** @deprecated Use \OCP\API::ADMIN_AUTH instead */ const ADMIN_AUTH = 3; /** * API Response Codes */ /** @deprecated Use \OCP\API::RESPOND_UNAUTHORISED instead */ const RESPOND_UNAUTHORISED = 997; /** @deprecated Use \OCP\API::RESPOND_SERVER_ERROR instead */ const RESPOND_SERVER_ERROR = 996; /** @deprecated Use \OCP\API::RESPOND_NOT_FOUND instead */ const RESPOND_NOT_FOUND = 998; /** @deprecated Use \OCP\API::RESPOND_UNKNOWN_ERROR instead */ const RESPOND_UNKNOWN_ERROR = 999; /** * api actions */ protected static $actions = array(); private static $logoutRequired = false; private static $isLoggedIn = false; /** * registers an api call * @param string $method the http method * @param string $url the url to match * @param callable $action the function to run * @param string $app the id of the app registering the call * @param int $authLevel the level of authentication required for the call * @param array $defaults * @param array $requirements */ public static function register($method, $url, $action, $app, $authLevel = API::USER_AUTH, $defaults = array(), $requirements = array()) { $name = strtolower($method).$url; $name = str_replace(array('/', '{', '}'), '_', $name); if(!isset(self::$actions[$name])) { $oldCollection = OC::$server->getRouter()->getCurrentCollection(); OC::$server->getRouter()->useCollection('ocs'); OC::$server->getRouter()->create($name, $url) ->method($method) ->defaults($defaults) ->requirements($requirements) ->action('OC_API', 'call'); self::$actions[$name] = array(); OC::$server->getRouter()->useCollection($oldCollection); } self::$actions[$name][] = array('app' => $app, 'action' => $action, 'authlevel' => $authLevel); } /** * handles an api call * @param array $parameters */ public static function call($parameters) { $request = \OC::$server->getRequest(); $method = $request->getMethod(); // Prepare the request variables if($method === 'PUT') { $parameters['_put'] = $request->getParams(); } else if($method === 'DELETE') { $parameters['_delete'] = $request->getParams(); } $name = $parameters['_route']; // Foreach registered action $responses = array(); foreach(self::$actions[$name] as $action) { // Check authentication and availability if(!self::isAuthorised($action)) { $responses[] = array( 'app' => $action['app'], 'response' => new OC_OCS_Result(null, API::RESPOND_UNAUTHORISED, 'Unauthorised'), 'shipped' => OC_App::isShipped($action['app']), ); continue; } if(!is_callable($action['action'])) { $responses[] = array( 'app' => $action['app'], 'response' => new OC_OCS_Result(null, API::RESPOND_NOT_FOUND, 'Api method not found'), 'shipped' => OC_App::isShipped($action['app']), ); continue; } // Run the action $responses[] = array( 'app' => $action['app'], 'response' => call_user_func($action['action'], $parameters), 'shipped' => OC_App::isShipped($action['app']), ); } $response = self::mergeResponses($responses); $format = self::requestedFormat(); if (self::$logoutRequired) { \OC::$server->getUserSession()->logout(); } self::respond($response, $format); } /** * merge the returned result objects into one response * @param array $responses * @return OC_OCS_Result */ public static function mergeResponses($responses) { // Sort into shipped and third-party $shipped = array( 'succeeded' => array(), 'failed' => array(), ); $thirdparty = array( 'succeeded' => array(), 'failed' => array(), ); foreach($responses as $response) { if($response['shipped'] || ($response['app'] === 'core')) { if($response['response']->succeeded()) { $shipped['succeeded'][$response['app']] = $response; } else { $shipped['failed'][$response['app']] = $response; } } else { if($response['response']->succeeded()) { $thirdparty['succeeded'][$response['app']] = $response; } else { $thirdparty['failed'][$response['app']] = $response; } } } // Remove any error responses if there is one shipped response that succeeded if(!empty($shipped['failed'])) { // Which shipped response do we use if they all failed? // They may have failed for different reasons (different status codes) // Which response code should we return? // Maybe any that are not \OCP\API::RESPOND_SERVER_ERROR // Merge failed responses if more than one $data = array(); foreach($shipped['failed'] as $failure) { $data = array_merge_recursive($data, $failure['response']->getData()); } $picked = reset($shipped['failed']); $code = $picked['response']->getStatusCode(); $meta = $picked['response']->getMeta(); $headers = $picked['response']->getHeaders(); $response = new OC_OCS_Result($data, $code, $meta['message'], $headers); return $response; } elseif(!empty($shipped['succeeded'])) { $responses = array_merge($shipped['succeeded'], $thirdparty['succeeded']); } elseif(!empty($thirdparty['failed'])) { // Merge failed responses if more than one $data = array(); foreach($thirdparty['failed'] as $failure) { $data = array_merge_recursive($data, $failure['response']->getData()); } $picked = reset($thirdparty['failed']); $code = $picked['response']->getStatusCode(); $meta = $picked['response']->getMeta(); $headers = $picked['response']->getHeaders(); $response = new OC_OCS_Result($data, $code, $meta['message'], $headers); return $response; } else { $responses = $thirdparty['succeeded']; } // Merge the successful responses $data = []; $codes = []; $header = []; foreach($responses as $response) { if($response['shipped']) { $data = array_merge_recursive($response['response']->getData(), $data); } else { $data = array_merge_recursive($data, $response['response']->getData()); } $header = array_merge_recursive($header, $response['response']->getHeaders()); $codes[] = ['code' => $response['response']->getStatusCode(), 'meta' => $response['response']->getMeta()]; } // Use any non 100 status codes $statusCode = 100; $statusMessage = null; foreach($codes as $code) { if($code['code'] != 100) { $statusCode = $code['code']; $statusMessage = $code['meta']['message']; break; } } return new OC_OCS_Result($data, $statusCode, $statusMessage, $header); } /** * authenticate the api call * @param array $action the action details as supplied to OC_API::register() * @return bool */ private static function isAuthorised($action) { $level = $action['authlevel']; switch($level) { case API::GUEST_AUTH: // Anyone can access return true; case API::USER_AUTH: // User required return self::loginUser(); case API::SUBADMIN_AUTH: // Check for subadmin $user = self::loginUser(); if(!$user) { return false; } else { $userObject = \OC::$server->getUserSession()->getUser(); if($userObject === null) { return false; } $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject); $admin = OC_User::isAdminUser($user); if($isSubAdmin || $admin) { return true; } else { return false; } } case API::ADMIN_AUTH: // Check for admin $user = self::loginUser(); if(!$user) { return false; } else { return OC_User::isAdminUser($user); } default: // oops looks like invalid level supplied return false; } } /** * http basic auth * @return string|false (username, or false on failure) */ private static function loginUser() { if(self::$isLoggedIn === true) { return \OC_User::getUser(); } // reuse existing login $loggedIn = \OC::$server->getUserSession()->isLoggedIn(); if ($loggedIn === true) { if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { // Do not allow access to OCS until the 2FA challenge was solved successfully return false; } $ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false; if ($ocsApiRequest) { // initialize the user's filesystem \OC_Util::setupFS(\OC_User::getUser()); self::$isLoggedIn = true; return OC_User::getUser(); } return false; } // basic auth - because OC_User::login will create a new session we shall only try to login // if user and pass are set $userSession = \OC::$server->getUserSession(); $request = \OC::$server->getRequest(); try { if ($userSession->tryTokenLogin($request) || $userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) { self::$logoutRequired = true; } else { return false; } // initialize the user's filesystem \OC_Util::setupFS(\OC_User::getUser()); self::$isLoggedIn = true; return \OC_User::getUser(); } catch (\OC\User\LoginException $e) { return false; } } /** * respond to a call * @param OC_OCS_Result $result * @param string $format the format xml|json */ public static function respond($result, $format='xml') { $request = \OC::$server->getRequest(); // Send 401 headers if unauthorised if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) { // If request comes from JS return dummy auth request if($request->getHeader('X-Requested-With') === 'XMLHttpRequest') { header('WWW-Authenticate: DummyBasic realm="Authorisation Required"'); } else { header('WWW-Authenticate: Basic realm="Authorisation Required"'); } header('HTTP/1.0 401 Unauthorized'); } foreach($result->getHeaders() as $name => $value) { header($name . ': ' . $value); } $meta = $result->getMeta(); $data = $result->getData(); if (self::isV2($request)) { $statusCode = self::mapStatusCodes($result->getStatusCode()); if (!is_null($statusCode)) { $meta['statuscode'] = $statusCode; OC_Response::setStatus($statusCode); } } self::setContentType($format); $body = self::renderResult($format, $meta, $data); echo $body; } /** * @param XMLWriter $writer */ private static function toXML($array, $writer) { foreach($array as $k => $v) { if ($k[0] === '@') { $writer->writeAttribute(substr($k, 1), $v); continue; } else if (is_numeric($k)) { $k = 'element'; } if(is_array($v)) { $writer->startElement($k); self::toXML($v, $writer); $writer->endElement(); } else { $writer->writeElement($k, $v); } } } /** * @return string */ public static function requestedFormat() { $formats = array('json', 'xml'); $format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml'; return $format; } /** * Based on the requested format the response content type is set * @param string $format */ public static function setContentType($format = null) { $format = is_null($format) ? self::requestedFormat() : $format; if ($format === 'xml') { header('Content-type: text/xml; charset=UTF-8'); return; } if ($format === 'json') { header('Content-Type: application/json; charset=utf-8'); return; } header('Content-Type: application/octet-stream; charset=utf-8'); } /** * @param \OCP\IRequest $request * @return bool */ protected static function isV2(\OCP\IRequest $request) { $script = $request->getScriptName(); return substr($script, -11) === '/ocs/v2.php'; } /** * @param integer $sc * @return int */ public static function mapStatusCodes($sc) { switch ($sc) { case API::RESPOND_NOT_FOUND: return Http::STATUS_NOT_FOUND; case API::RESPOND_SERVER_ERROR: return Http::STATUS_INTERNAL_SERVER_ERROR; case API::RESPOND_UNKNOWN_ERROR: return Http::STATUS_INTERNAL_SERVER_ERROR; case API::RESPOND_UNAUTHORISED: // already handled for v1 return null; case 100: return Http::STATUS_OK; } // any 2xx, 4xx and 5xx will be used as is if ($sc >= 200 && $sc < 600) { return $sc; } return Http::STATUS_BAD_REQUEST; } /** * @param string $format * @return string */ public static function renderResult($format, $meta, $data) { $response = array( 'ocs' => array( 'meta' => $meta, 'data' => $data, ), ); if ($format == 'json') { return OC_JSON::encode($response); } $writer = new XMLWriter(); $writer->openMemory(); $writer->setIndent(true); $writer->startDocument(); self::toXML($response, $writer); $writer->endDocument(); return $writer->outputMemory(true); } } private/legacy/user.php 0000604 00000040552 15247130452 0011156 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Aldo "xoen" Giambelluca <xoen@xoen.org> * @author Andreas Fischer <bantu@owncloud.com> * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bartek Przybylski <bart.p.pl@gmail.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Christoph Wurst <christoph@owncloud.com> * @author Georg Ehrke <georg@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author shkdee <louis.traynard@m4x.org> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * This class provides wrapper methods for user management. Multiple backends are * supported. User management operations are delegated to the configured backend for * execution. * * Note that &run is deprecated and won't work anymore. * * Hooks provided: * pre_createUser(&run, uid, password) * post_createUser(uid, password) * pre_deleteUser(&run, uid) * post_deleteUser(uid) * pre_setPassword(&run, uid, password, recoveryPassword) * post_setPassword(uid, password, recoveryPassword) * pre_login(&run, uid, password) * post_login(uid) * logout() */ class OC_User { /** * @return \OC\User\Session */ public static function getUserSession() { return OC::$server->getUserSession(); } private static $_usedBackends = array(); private static $_setupedBackends = array(); // bool, stores if a user want to access a resource anonymously, e.g if they open a public link private static $incognitoMode = false; /** * Adds the backend to the list of used backends * * @param string|\OCP\UserInterface $backend default: database The backend to use for user management * @return bool * * Set the User Authentication Module */ public static function useBackend($backend = 'database') { if ($backend instanceof \OCP\UserInterface) { self::$_usedBackends[get_class($backend)] = $backend; \OC::$server->getUserManager()->registerBackend($backend); } else { // You'll never know what happens if (null === $backend OR !is_string($backend)) { $backend = 'database'; } // Load backend switch ($backend) { case 'database': case 'mysql': case 'sqlite': \OCP\Util::writeLog('core', 'Adding user backend ' . $backend . '.', \OCP\Util::DEBUG); self::$_usedBackends[$backend] = new \OC\User\Database(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; case 'dummy': self::$_usedBackends[$backend] = new \Test\Util\User\Dummy(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; default: \OCP\Util::writeLog('core', 'Adding default user backend ' . $backend . '.', \OCP\Util::DEBUG); $className = 'OC_USER_' . strtoupper($backend); self::$_usedBackends[$backend] = new $className(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; } } return true; } /** * remove all used backends */ public static function clearBackends() { self::$_usedBackends = array(); \OC::$server->getUserManager()->clearBackends(); } /** * setup the configured backends in config.php */ public static function setupBackends() { OC_App::loadApps(['prelogin']); $backends = \OC::$server->getSystemConfig()->getValue('user_backends', []); if (isset($backends['default']) && !$backends['default']) { // clear default backends self::clearBackends(); } foreach ($backends as $i => $config) { if (!is_array($config)) { continue; } $class = $config['class']; $arguments = $config['arguments']; if (class_exists($class)) { if (array_search($i, self::$_setupedBackends) === false) { // make a reflection object $reflectionObj = new ReflectionClass($class); // use Reflection to create a new instance, using the $args $backend = $reflectionObj->newInstanceArgs($arguments); self::useBackend($backend); self::$_setupedBackends[] = $i; } else { \OCP\Util::writeLog('core', 'User backend ' . $class . ' already initialized.', \OCP\Util::DEBUG); } } else { \OCP\Util::writeLog('core', 'User backend ' . $class . ' not found.', \OCP\Util::ERROR); } } } /** * Try to login a user using the magic cookie (remember login) * * @deprecated use \OCP\IUserSession::loginWithCookie() * @param string $uid The username of the user to log in * @param string $token * @param string $oldSessionId * @return bool */ public static function loginWithCookie($uid, $token, $oldSessionId) { return self::getUserSession()->loginWithCookie($uid, $token, $oldSessionId); } /** * Try to login a user, assuming authentication * has already happened (e.g. via Single Sign On). * * Log in a user and regenerate a new session. * * @param \OCP\Authentication\IApacheBackend $backend * @return bool */ public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) { $uid = $backend->getCurrentUserId(); $run = true; OC_Hook::emit("OC_User", "pre_login", array("run" => &$run, "uid" => $uid)); if ($uid) { if (self::getUser() !== $uid) { self::setUserId($uid); $setUidAsDisplayName = true; if($backend instanceof \OCP\UserInterface && $backend->implementsActions(OC_User_Backend::GET_DISPLAYNAME)) { $backendDisplayName = $backend->getDisplayName($uid); if(is_string($backendDisplayName) && trim($backendDisplayName) !== '') { $setUidAsDisplayName = false; } } if($setUidAsDisplayName) { self::setDisplayName($uid); } $userSession = self::getUserSession(); $userSession->setLoginName($uid); $request = OC::$server->getRequest(); $userSession->createSessionToken($request, $uid, $uid); // setup the filesystem OC_Util::setupFS($uid); // first call the post_login hooks, the login-process needs to be // completed before we can safely create the users folder. // For example encryption needs to initialize the users keys first // before we can create the user folder with the skeleton files OC_Hook::emit("OC_User", "post_login", array("uid" => $uid, 'password' => '')); //trigger creation of user home and /files folder \OC::$server->getUserFolder($uid); } return true; } return false; } /** * Verify with Apache whether user is authenticated. * * @return boolean|null * true: authenticated * false: not authenticated * null: not handled / no backend available */ public static function handleApacheAuth() { $backend = self::findFirstActiveUsedBackend(); if ($backend) { OC_App::loadApps(); //setup extra user backends self::setupBackends(); self::unsetMagicInCookie(); return self::loginWithApache($backend); } return null; } /** * Sets user id for session and triggers emit * * @param string $uid */ public static function setUserId($uid) { $userSession = \OC::$server->getUserSession(); $userManager = \OC::$server->getUserManager(); if ($user = $userManager->get($uid)) { $userSession->setUser($user); } else { \OC::$server->getSession()->set('user_id', $uid); } } /** * Sets user display name for session * * @param string $uid * @param string $displayName * @return bool Whether the display name could get set */ public static function setDisplayName($uid, $displayName = null) { if (is_null($displayName)) { $displayName = $uid; } $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->setDisplayName($displayName); } else { return false; } } /** * Check if the user is logged in, considers also the HTTP basic credentials * * @deprecated use \OC::$server->getUserSession()->isLoggedIn() * @return bool */ public static function isLoggedIn() { return \OC::$server->getUserSession()->isLoggedIn(); } /** * set incognito mode, e.g. if a user wants to open a public link * * @param bool $status */ public static function setIncognitoMode($status) { self::$incognitoMode = $status; } /** * get incognito mode status * * @return bool */ public static function isIncognitoMode() { return self::$incognitoMode; } /** * Returns the current logout URL valid for the currently logged-in user * * @param \OCP\IURLGenerator $urlGenerator * @return string */ public static function getLogoutUrl(\OCP\IURLGenerator $urlGenerator) { $backend = self::findFirstActiveUsedBackend(); if ($backend) { return $backend->getLogoutUrl(); } $logoutUrl = $urlGenerator->linkToRouteAbsolute( 'core.login.logout', [ 'requesttoken' => \OCP\Util::callRegister(), ] ); return $logoutUrl; } /** * Check if the user is an admin user * * @param string $uid uid of the admin * @return bool */ public static function isAdminUser($uid) { $group = \OC::$server->getGroupManager()->get('admin'); $user = \OC::$server->getUserManager()->get($uid); if ($group && $user && $group->inGroup($user) && self::$incognitoMode === false) { return true; } return false; } /** * get the user id of the user currently logged in. * * @return string|bool uid or false */ public static function getUser() { $uid = \OC::$server->getSession() ? \OC::$server->getSession()->get('user_id') : null; if (!is_null($uid) && self::$incognitoMode === false) { return $uid; } else { return false; } } /** * get the display name of the user currently logged in. * * @param string $uid * @return string uid or false */ public static function getDisplayName($uid = null) { if ($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->getDisplayName(); } else { return $uid; } } else { $user = self::getUserSession()->getUser(); if ($user) { return $user->getDisplayName(); } else { return false; } } } /** * Autogenerate a password * * @return string * * generates a password */ public static function generatePassword() { return \OC::$server->getSecureRandom()->generate(30); } /** * Set password * * @param string $uid The username * @param string $password The new password * @param string $recoveryPassword for the encryption app to reset encryption keys * @return bool * * Change the password of a user */ public static function setPassword($uid, $password, $recoveryPassword = null) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->setPassword($password, $recoveryPassword); } else { return false; } } /** * Check whether user can change his avatar * * @param string $uid The username * @return bool * * Check whether a specified user can change his avatar */ public static function canUserChangeAvatar($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->canChangeAvatar(); } else { return false; } } /** * Check whether user can change his password * * @param string $uid The username * @return bool * * Check whether a specified user can change his password */ public static function canUserChangePassword($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->canChangePassword(); } else { return false; } } /** * Check whether user can change his display name * * @param string $uid The username * @return bool * * Check whether a specified user can change his display name */ public static function canUserChangeDisplayName($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->canChangeDisplayName(); } else { return false; } } /** * Check if the password is correct * * @param string $uid The username * @param string $password The password * @return string|false user id a string on success, false otherwise * * Check if the password is correct without logging in the user * returns the user id or false */ public static function checkPassword($uid, $password) { $manager = \OC::$server->getUserManager(); $username = $manager->checkPassword($uid, $password); if ($username !== false) { return $username->getUID(); } return false; } /** * @param string $uid The username * @return string * * returns the path to the users home directory * @deprecated Use \OC::$server->getUserManager->getHome() */ public static function getHome($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->getHome(); } else { return \OC::$server->getSystemConfig()->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $uid; } } /** * Get a list of all users * * @return array an array of all uids * * Get a list of all users. * @param string $search * @param integer $limit * @param integer $offset */ public static function getUsers($search = '', $limit = null, $offset = null) { $users = \OC::$server->getUserManager()->search($search, $limit, $offset); $uids = array(); foreach ($users as $user) { $uids[] = $user->getUID(); } return $uids; } /** * Get a list of all users display name * * @param string $search * @param int $limit * @param int $offset * @return array associative array with all display names (value) and corresponding uids (key) * * Get a list of all display names and user ids. * @deprecated Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead. */ public static function getDisplayNames($search = '', $limit = null, $offset = null) { $displayNames = array(); $users = \OC::$server->getUserManager()->searchDisplayName($search, $limit, $offset); foreach ($users as $user) { $displayNames[$user->getUID()] = $user->getDisplayName(); } return $displayNames; } /** * check if a user exists * * @param string $uid the username * @return boolean */ public static function userExists($uid) { return \OC::$server->getUserManager()->userExists($uid); } /** * disables a user * * @param string $uid the user to disable */ public static function disableUser($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { $user->setEnabled(false); } } /** * enable a user * * @param string $uid */ public static function enableUser($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { $user->setEnabled(true); } } /** * checks if a user is enabled * * @param string $uid * @return bool */ public static function isEnabled($uid) { $user = \OC::$server->getUserManager()->get($uid); if ($user) { return $user->isEnabled(); } else { return false; } } /** * Set cookie value to use in next page load * * @param string $username username to be set * @param string $token */ public static function setMagicInCookie($username, $token) { self::getUserSession()->setMagicInCookie($username, $token); } /** * Remove cookie for "remember username" */ public static function unsetMagicInCookie() { self::getUserSession()->unsetMagicInCookie(); } /** * Returns the first active backend from self::$_usedBackends. * * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend */ private static function findFirstActiveUsedBackend() { foreach (self::$_usedBackends as $backend) { if ($backend instanceof OCP\Authentication\IApacheBackend) { if ($backend->isSessionActive()) { return $backend; } } } return null; } } private/legacy/ocs/result.php 0000604 00000001620 15247130452 0012273 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * @deprecated Since 9.1.0 use \OC\OCS\Result */ class OC_OCS_Result extends \OC\OCS\Result { } private/legacy/ocs/privatedata.php 0000604 00000001637 15247130452 0013271 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * @deprecated Since 9.1.0 use \OC\OCS\PrivateData */ class OC_OCS_Privatedata extends \OC\OCS\PrivateData { } private/legacy/user/interface.php 0000604 00000001763 15247130452 0013117 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Interface OC_User_Interface * @deprecated use the public \OCP\UserInterface instead */ interface OC_User_Interface extends \OCP\UserInterface {} private/legacy/user/backend.php 0000604 00000004623 15247130452 0012544 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Aldo "xoen" Giambelluca <xoen@xoen.org> * @author Dominik Schmidt <dev@dominik-schmidt.de> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sam Tuke <mail@samtuke.com> * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * error code for functions not provided by the user backend * @deprecated Use \OC_User_Backend::NOT_IMPLEMENTED instead */ define('OC_USER_BACKEND_NOT_IMPLEMENTED', -501); /** * actions that user backends can define */ /** @deprecated Use \OC_User_Backend::CREATE_USER instead */ define('OC_USER_BACKEND_CREATE_USER', 1 << 0); /** @deprecated Use \OC_User_Backend::SET_PASSWORD instead */ define('OC_USER_BACKEND_SET_PASSWORD', 1 << 4); /** @deprecated Use \OC_User_Backend::CHECK_PASSWORD instead */ define('OC_USER_BACKEND_CHECK_PASSWORD', 1 << 8); /** @deprecated Use \OC_User_Backend::GET_HOME instead */ define('OC_USER_BACKEND_GET_HOME', 1 << 12); /** @deprecated Use \OC_User_Backend::GET_DISPLAYNAME instead */ define('OC_USER_BACKEND_GET_DISPLAYNAME', 1 << 16); /** @deprecated Use \OC_User_Backend::SET_DISPLAYNAME instead */ define('OC_USER_BACKEND_SET_DISPLAYNAME', 1 << 20); /** @deprecated Use \OC_User_Backend::PROVIDE_AVATAR instead */ define('OC_USER_BACKEND_PROVIDE_AVATAR', 1 << 24); /** @deprecated Use \OC_User_Backend::COUNT_USERS instead */ define('OC_USER_BACKEND_COUNT_USERS', 1 << 28); /** * Abstract base class for user management. Provides methods for querying backend * capabilities. */ abstract class OC_User_Backend extends \OC\User\Backend implements \OCP\UserInterface { } private/legacy/response.php 0000604 00000022002 15247130452 0012024 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ class OC_Response { const STATUS_FOUND = 304; const STATUS_NOT_MODIFIED = 304; const STATUS_TEMPORARY_REDIRECT = 307; const STATUS_BAD_REQUEST = 400; const STATUS_FORBIDDEN = 403; const STATUS_NOT_FOUND = 404; const STATUS_INTERNAL_SERVER_ERROR = 500; const STATUS_SERVICE_UNAVAILABLE = 503; /** * Enable response caching by sending correct HTTP headers * @param integer $cache_time time to cache the response * >0 cache time in seconds * 0 and <0 enable default browser caching * null cache indefinitely */ static public function enableCaching($cache_time = null) { if (is_numeric($cache_time)) { header('Pragma: public');// enable caching in IE if ($cache_time > 0) { self::setExpiresHeader('PT'.$cache_time.'S'); header('Cache-Control: max-age='.$cache_time.', must-revalidate'); } else { self::setExpiresHeader(0); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); } } else { header('Cache-Control: cache'); header('Pragma: cache'); } } /** * disable browser caching * @see enableCaching with cache_time = 0 */ static public function disableCaching() { self::enableCaching(0); } /** * Set response status * @param int $status a HTTP status code, see also the STATUS constants */ static public function setStatus($status) { $protocol = \OC::$server->getRequest()->getHttpProtocol(); switch($status) { case self::STATUS_NOT_MODIFIED: $status = $status . ' Not Modified'; break; case self::STATUS_TEMPORARY_REDIRECT: if ($protocol == 'HTTP/1.1') { $status = $status . ' Temporary Redirect'; break; } else { $status = self::STATUS_FOUND; // fallthrough } case self::STATUS_FOUND; $status = $status . ' Found'; break; case self::STATUS_NOT_FOUND; $status = $status . ' Not Found'; break; case self::STATUS_INTERNAL_SERVER_ERROR; $status = $status . ' Internal Server Error'; break; case self::STATUS_SERVICE_UNAVAILABLE; $status = $status . ' Service Unavailable'; break; } header($protocol.' '.$status); } /** * Send redirect response * @param string $location to redirect to */ static public function redirect($location) { self::setStatus(self::STATUS_TEMPORARY_REDIRECT); header('Location: '.$location); } /** * Set response expire time * @param string|DateTime $expires date-time when the response expires * string for DateInterval from now * DateTime object when to expire response */ static public function setExpiresHeader($expires) { if (is_string($expires) && $expires[0] == 'P') { $interval = $expires; $expires = new DateTime('now'); $expires->add(new DateInterval($interval)); } if ($expires instanceof DateTime) { $expires->setTimezone(new DateTimeZone('GMT')); $expires = $expires->format(DateTime::RFC2822); } header('Expires: '.$expires); } /** * Checks and set ETag header, when the request matches sends a * 'not modified' response * @param string $etag token to use for modification check */ static public function setETagHeader($etag) { if (empty($etag)) { return; } $etag = '"'.$etag.'"'; if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { self::setStatus(self::STATUS_NOT_MODIFIED); exit; } header('ETag: '.$etag); } /** * Checks and set Last-Modified header, when the request matches sends a * 'not modified' response * @param int|DateTime|string $lastModified time when the response was last modified */ static public function setLastModifiedHeader($lastModified) { if (empty($lastModified)) { return; } if (is_int($lastModified)) { $lastModified = gmdate(DateTime::RFC2822, $lastModified); } if ($lastModified instanceof DateTime) { $lastModified = $lastModified->format(DateTime::RFC2822); } if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && trim($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) { self::setStatus(self::STATUS_NOT_MODIFIED); exit; } header('Last-Modified: '.$lastModified); } /** * Sets the content disposition header (with possible workarounds) * @param string $filename file name * @param string $type disposition type, either 'attachment' or 'inline' */ static public function setContentDispositionHeader( $filename, $type = 'attachment' ) { if (\OC::$server->getRequest()->isUserAgent( [ \OC\AppFramework\Http\Request::USER_AGENT_IE, \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME, \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX, ])) { header( 'Content-Disposition: ' . rawurlencode($type) . '; filename="' . rawurlencode( $filename ) . '"' ); } else { header( 'Content-Disposition: ' . rawurlencode($type) . '; filename*=UTF-8\'\'' . rawurlencode( $filename ) . '; filename="' . rawurlencode( $filename ) . '"' ); } } /** * Sets the content length header (with possible workarounds) * @param string|int|float $length Length to be sent */ static public function setContentLengthHeader($length) { if (PHP_INT_SIZE === 4) { if ($length > PHP_INT_MAX && stripos(PHP_SAPI, 'apache') === 0) { // Apache PHP SAPI casts Content-Length headers to PHP integers. // This enforces a limit of PHP_INT_MAX (2147483647 on 32-bit // platforms). So, if the length is greater than PHP_INT_MAX, // we just do not send a Content-Length header to prevent // bodies from being received incompletely. return; } // Convert signed integer or float to unsigned base-10 string. $lfh = new \OC\LargeFileHelper; $length = $lfh->formatUnsignedInteger($length); } header('Content-Length: '.$length); } /** * Send file as response, checking and setting caching headers * @param string $filepath of file to send * @deprecated 8.1.0 - Use \OCP\AppFramework\Http\StreamResponse or another AppFramework controller instead */ static public function sendFile($filepath) { $fp = fopen($filepath, 'rb'); if ($fp) { self::setLastModifiedHeader(filemtime($filepath)); self::setETagHeader(md5_file($filepath)); self::setContentLengthHeader(filesize($filepath)); fpassthru($fp); } else { self::setStatus(self::STATUS_NOT_FOUND); } } /** * This function adds some security related headers to all requests served via base.php * The implementation of this function has to happen here to ensure that all third-party * components (e.g. SabreDAV) also benefit from this headers. */ public static function addSecurityHeaders() { /** * FIXME: Content Security Policy for legacy ownCloud components. This * can be removed once \OCP\AppFramework\Http\Response from the AppFramework * is used everywhere. * @see \OCP\AppFramework\Http\Response::getHeaders */ $policy = 'default-src \'self\'; ' . 'script-src \'self\' \'unsafe-eval\' \'nonce-'.\OC::$server->getContentSecurityPolicyNonceManager()->getNonce().'\'; ' . 'style-src \'self\' \'unsafe-inline\'; ' . 'frame-src *; ' . 'img-src * data: blob:; ' . 'font-src \'self\' data:; ' . 'media-src *; ' . 'connect-src *; ' . 'object-src \'none\'; ' . 'base-uri \'self\'; '; header('Content-Security-Policy:' . $policy); header('X-Frame-Options: SAMEORIGIN'); // Disallow iFraming from other domains // Send fallback headers for installations that don't have the possibility to send // custom headers on the webserver side if(getenv('modHeadersAvailable') !== 'true') { header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE header('X-Robots-Tag: none'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag header('X-Download-Options: noopen'); // https://msdn.microsoft.com/en-us/library/jj542450(v=vs.85).aspx header('X-Permitted-Cross-Domain-Policies: none'); // https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html } } } private/legacy/eventsource.php 0000604 00000007527 15247130452 0012547 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Felix Moeller <mail@felixmoeller.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) * includes a fallback for older browsers and IE * * use server side events with caution, to many open requests can hang the server */ class OC_EventSource implements \OCP\IEventSource { /** * @var bool */ private $fallback; /** * @var int */ private $fallBackId = 0; /** * @var bool */ private $started = false; protected function init() { if ($this->started) { return; } $this->started = true; // prevent php output buffering, caching and nginx buffering OC_Util::obEnd(); header('Cache-Control: no-cache'); header('X-Accel-Buffering: no'); $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true'; if ($this->fallback) { $this->fallBackId = (int)$_GET['fallback_id']; /** * FIXME: The default content-security-policy of ownCloud forbids inline * JavaScript for security reasons. IE starting on Windows 10 will * however also obey the CSP which will break the event source fallback. * * As a workaround thus we set a custom policy which allows the execution * of inline JavaScript. * * @link https://github.com/owncloud/core/issues/14286 */ header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'"); header("Content-Type: text/html"); echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy } else { header("Content-Type: text/event-stream"); } if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { header('Location: '.\OC::$WEBROOT); exit(); } if (!(\OC::$server->getRequest()->passesCSRFCheck())) { $this->send('error', 'Possible CSRF attack. Connection will be closed.'); $this->close(); exit(); } flush(); } /** * send a message to the client * * @param string $type * @param mixed $data * * @throws \BadMethodCallException * if only one parameter is given, a typeless message will be send with that parameter as data */ public function send($type, $data = null) { if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) { throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')'); } $this->init(); if (is_null($data)) { $data = $type; $type = null; } if ($this->fallback) { $response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack(' . $this->fallBackId . ',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL; echo $response; } else { if ($type) { echo 'event: ' . $type . PHP_EOL; } echo 'data: ' . OCP\JSON::encode($data) . PHP_EOL; } echo PHP_EOL; flush(); } /** * close the connection of the event source */ public function close() { $this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it } } private/legacy/group/backend.php 0000604 00000004133 15247130452 0012716 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * error code for functions not provided by the group backend * @deprecated Use \OC_Group_Backend::NOT_IMPLEMENTED instead */ define('OC_GROUP_BACKEND_NOT_IMPLEMENTED', -501); /** * actions that user backends can define */ /** @deprecated Use \OC_Group_Backend::CREATE_GROUP instead */ define('OC_GROUP_BACKEND_CREATE_GROUP', 0x00000001); /** @deprecated Use \OC_Group_Backend::DELETE_GROUP instead */ define('OC_GROUP_BACKEND_DELETE_GROUP', 0x00000010); /** @deprecated Use \OC_Group_Backend::ADD_TO_GROUP instead */ define('OC_GROUP_BACKEND_ADD_TO_GROUP', 0x00000100); /** @deprecated Use \OC_Group_Backend::REMOVE_FROM_GOUP instead */ define('OC_GROUP_BACKEND_REMOVE_FROM_GOUP', 0x00001000); /** @deprecated Obsolete */ define('OC_GROUP_BACKEND_GET_DISPLAYNAME', 0x00010000); //OBSOLETE /** @deprecated Use \OC_Group_Backend::COUNT_USERS instead */ define('OC_GROUP_BACKEND_COUNT_USERS', 0x00100000); /** * Abstract base class for user management * @deprecated Since 9.1.0 use \OC\Group\Backend */ abstract class OC_Group_Backend extends \OC\Group\Backend { } private/legacy/files.php 0000604 00000032432 15247130452 0011300 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Clark Tomlinson <fallen013@gmail.com> * @author Frank Karlitschek <frank@karlitschek.de> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Nicolai Ehemann <en@enlightened.de> * @author Piotr Filiciak <piotr@filiciak.pl> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thibaut GRIDEL <tgridel@free.fr> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ use OC\Files\View; use OC\Streamer; use OCP\Lock\ILockingProvider; /** * Class for file server access * */ class OC_Files { const FILE = 1; const ZIP_FILES = 2; const ZIP_DIR = 3; const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB private static $multipartBoundary = ''; /** * @return string */ private static function getBoundary() { if (empty(self::$multipartBoundary)) { self::$multipartBoundary = md5(mt_rand()); } return self::$multipartBoundary; } /** * @param string $filename * @param string $name * @param array $rangeArray ('from'=>int,'to'=>int), ... */ private static function sendHeaders($filename, $name, array $rangeArray) { OC_Response::setContentDispositionHeader($name, 'attachment'); header('Content-Transfer-Encoding: binary', true); OC_Response::disableCaching(); $fileSize = \OC\Files\Filesystem::filesize($filename); $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); if ($fileSize > -1) { if (!empty($rangeArray)) { header('HTTP/1.1 206 Partial Content', true); header('Accept-Ranges: bytes', true); if (count($rangeArray) > 1) { $type = 'multipart/byteranges; boundary='.self::getBoundary(); // no Content-Length header here } else { header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true); OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1); } } else { OC_Response::setContentLengthHeader($fileSize); } } header('Content-Type: '.$type, true); } /** * return the content of a file or return a zip file containing multiple files * * @param string $dir * @param string $files ; separated list of files to download * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header */ public static function get($dir, $files, $params = null) { $view = \OC\Files\Filesystem::getView(); $getType = self::FILE; $filename = $dir; try { if (is_array($files) && count($files) === 1) { $files = $files[0]; } if (!is_array($files)) { $filename = $dir . '/' . $files; if (!$view->is_dir($filename)) { self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params); return; } } $name = 'download'; if (is_array($files)) { $getType = self::ZIP_FILES; $basename = basename($dir); if ($basename) { $name = $basename; } $filename = $dir . '/' . $name; } else { $filename = $dir . '/' . $files; $getType = self::ZIP_DIR; // downloading root ? if ($files !== '') { $name = $files; } } $streamer = new Streamer(); OC_Util::obEnd(); self::lockFiles($view, $dir, $files); $streamer->sendHeaders($name); $executionTime = intval(OC::$server->getIniWrapper()->getNumeric('max_execution_time')); if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { @set_time_limit(0); } ignore_user_abort(true); if ($getType === self::ZIP_FILES) { foreach ($files as $file) { $file = $dir . '/' . $file; if (\OC\Files\Filesystem::is_file($file)) { $fileSize = \OC\Files\Filesystem::filesize($file); $fileTime = \OC\Files\Filesystem::filemtime($file); $fh = \OC\Files\Filesystem::fopen($file, 'r'); $streamer->addFileFromStream($fh, basename($file), $fileSize, $fileTime); fclose($fh); } elseif (\OC\Files\Filesystem::is_dir($file)) { $streamer->addDirRecursive($file); } } } elseif ($getType === self::ZIP_DIR) { $file = $dir . '/' . $files; $streamer->addDirRecursive($file); } $streamer->finalize(); set_time_limit($executionTime); self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); } catch (\OCP\Lock\LockedException $ex) { self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); OC::$server->getLogger()->logException($ex); $l = \OC::$server->getL10N('core'); $hint = method_exists($ex, 'getHint') ? $ex->getHint() : ''; \OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint); } catch (\OCP\Files\ForbiddenException $ex) { self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); OC::$server->getLogger()->logException($ex); $l = \OC::$server->getL10N('core'); \OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage()); } catch (\Exception $ex) { self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); OC::$server->getLogger()->logException($ex); $l = \OC::$server->getL10N('core'); $hint = method_exists($ex, 'getHint') ? $ex->getHint() : ''; \OC_Template::printErrorPage($l->t('Can\'t read file'), $hint); } } /** * @param string $rangeHeaderPos * @param int $fileSize * @return array $rangeArray ('from'=>int,'to'=>int), ... */ private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) { $rArray=explode(',', $rangeHeaderPos); $minOffset = 0; $ind = 0; $rangeArray = array(); foreach ($rArray as $value) { $ranges = explode('-', $value); if (is_numeric($ranges[0])) { if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999 $ranges[0] = $minOffset; } if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999 $ind--; $ranges[0] = $rangeArray[$ind]['from']; } } if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) { // case: x-x if ($ranges[1] >= $fileSize) { $ranges[1] = $fileSize-1; } $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize ); $minOffset = $ranges[1] + 1; if ($minOffset >= $fileSize) { break; } } elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) { // case: x- $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize ); break; } elseif (is_numeric($ranges[1])) { // case: -x if ($ranges[1] > $fileSize) { $ranges[1] = $fileSize; } $rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize ); break; } } return $rangeArray; } /** * @param View $view * @param string $name * @param string $dir * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header */ private static function getSingleFile($view, $dir, $name, $params) { $filename = $dir . '/' . $name; OC_Util::obEnd(); $view->lockFile($filename, ILockingProvider::LOCK_SHARED); $rangeArray = array(); if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') { $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), \OC\Files\Filesystem::filesize($filename)); } if (\OC\Files\Filesystem::isReadable($filename)) { self::sendHeaders($filename, $name, $rangeArray); } elseif (!\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.1 404 Not Found"); $tmpl = new OC_Template('', '404', 'guest'); $tmpl->printPage(); exit(); } else { header("HTTP/1.1 403 Forbidden"); die('403 Forbidden'); } if (isset($params['head']) && $params['head']) { return; } if (!empty($rangeArray)) { try { if (count($rangeArray) == 1) { $view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']); } else { // check if file is seekable (if not throw UnseekableException) // we have to check it before body contents $view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']); $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); foreach ($rangeArray as $range) { echo "\r\n--".self::getBoundary()."\r\n". "Content-type: ".$type."\r\n". "Content-range: bytes ".$range['from']."-".$range['to']."/".$range['size']."\r\n\r\n"; $view->readfilePart($filename, $range['from'], $range['to']); } echo "\r\n--".self::getBoundary()."--\r\n"; } } catch (\OCP\Files\UnseekableException $ex) { // file is unseekable header_remove('Accept-Ranges'); header_remove('Content-Range'); header("HTTP/1.1 200 OK"); self::sendHeaders($filename, $name, array()); $view->readfile($filename); } } else { $view->readfile($filename); } } /** * @param View $view * @param string $dir * @param string[]|string $files */ public static function lockFiles($view, $dir, $files) { if (!is_array($files)) { $file = $dir . '/' . $files; $files = [$file]; } foreach ($files as $file) { $file = $dir . '/' . $file; $view->lockFile($file, ILockingProvider::LOCK_SHARED); if ($view->is_dir($file)) { $contents = $view->getDirectoryContent($file); $contents = array_map(function($fileInfo) use ($file) { /** @var \OCP\Files\FileInfo $fileInfo */ return $file . '/' . $fileInfo->getName(); }, $contents); self::lockFiles($view, $dir, $contents); } } } /** * set the maximum upload size limit for apache hosts using .htaccess * * @param int $size file size in bytes * @param array $files override '.htaccess' and '.user.ini' locations * @return bool false on failure, size on success */ public static function setUploadLimit($size, $files = []) { //don't allow user to break his config $size = intval($size); if ($size < self::UPLOAD_MIN_LIMIT_BYTES) { return false; } $size = OC_Helper::phpFileSize($size); $phpValueKeys = array( 'upload_max_filesize', 'post_max_size' ); // default locations if not overridden by $files $files = array_merge([ '.htaccess' => OC::$SERVERROOT . '/.htaccess', '.user.ini' => OC::$SERVERROOT . '/.user.ini' ], $files); $updateFiles = [ $files['.htaccess'] => [ 'pattern' => '/php_value %1$s (\S)*/', 'setting' => 'php_value %1$s %2$s' ], $files['.user.ini'] => [ 'pattern' => '/%1$s=(\S)*/', 'setting' => '%1$s=%2$s' ] ]; $success = true; foreach ($updateFiles as $filename => $patternMap) { // suppress warnings from fopen() $handle = @fopen($filename, 'r+'); if (!$handle) { \OCP\Util::writeLog('files', 'Can\'t write upload limit to ' . $filename . '. Please check the file permissions', \OCP\Util::WARN); $success = false; continue; // try to update as many files as possible } $content = ''; while (!feof($handle)) { $content .= fread($handle, 1000); } foreach ($phpValueKeys as $key) { $pattern = vsprintf($patternMap['pattern'], [$key]); $setting = vsprintf($patternMap['setting'], [$key, $size]); $hasReplaced = 0; $newContent = preg_replace($pattern, $setting, $content, 2, $hasReplaced); if ($newContent !== null) { $content = $newContent; } if ($hasReplaced === 0) { $content .= "\n" . $setting; } } // write file back ftruncate($handle, 0); rewind($handle); fwrite($handle, $content); fclose($handle); } if ($success) { return OC_Helper::computerFileSize($size); } return false; } /** * @param string $dir * @param $files * @param integer $getType * @param View $view * @param string $filename */ private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) { if ($getType === self::FILE) { $view->unlockFile($filename, ILockingProvider::LOCK_SHARED); } if ($getType === self::ZIP_FILES) { foreach ($files as $file) { $file = $dir . '/' . $file; $view->unlockFile($file, ILockingProvider::LOCK_SHARED); } } if ($getType === self::ZIP_DIR) { $file = $dir . '/' . $files; $view->unlockFile($file, ILockingProvider::LOCK_SHARED); } } } private/legacy/template/functions.php 0000604 00000017025 15247130452 0014022 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Prints a sanitized string * @param string $string the string which will be escaped and printed */ function p($string) { print(\OCP\Util::sanitizeHTML($string)); } /** * Prints an unsanitized string - usage of this function may result into XSS. * Consider using p() instead. * @param string|array $string the string which will be printed as it is */ function print_unescaped($string) { print($string); } /** * Shortcut for adding scripts to a page * @param string $app the appname * @param string|string[] $file the filename, * if an array is given it will add all scripts */ function script($app, $file = null) { if(is_array($file)) { foreach($file as $f) { OC_Util::addScript($app, $f); } } else { OC_Util::addScript($app, $file); } } /** * Shortcut for adding vendor scripts to a page * @param string $app the appname * @param string|string[] $file the filename, * if an array is given it will add all scripts */ function vendor_script($app, $file = null) { if(is_array($file)) { foreach($file as $f) { OC_Util::addVendorScript($app, $f); } } else { OC_Util::addVendorScript($app, $file); } } /** * Shortcut for adding styles to a page * @param string $app the appname * @param string|string[] $file the filename, * if an array is given it will add all styles */ function style($app, $file = null) { if(is_array($file)) { foreach($file as $f) { OC_Util::addStyle($app, $f); } } else { OC_Util::addStyle($app, $file); } } /** * Shortcut for adding vendor styles to a page * @param string $app the appname * @param string|string[] $file the filename, * if an array is given it will add all styles */ function vendor_style($app, $file = null) { if(is_array($file)) { foreach($file as $f) { OC_Util::addVendorStyle($app, $f); } } else { OC_Util::addVendorStyle($app, $file); } } /** * Shortcut for adding translations to a page * @param string $app the appname * if an array is given it will add all styles */ function translation($app) { OC_Util::addTranslations($app); } /** * Shortcut for HTML imports * @param string $app the appname * @param string|string[] $file the path relative to the app's component folder, * if an array is given it will add all components */ function component($app, $file) { if(is_array($file)) { foreach($file as $f) { $url = link_to($app, 'component/' . $f . '.html'); OC_Util::addHeader('link', array('rel' => 'import', 'href' => $url)); } } else { $url = link_to($app, 'component/' . $file . '.html'); OC_Util::addHeader('link', array('rel' => 'import', 'href' => $url)); } } /** * make \OCP\IURLGenerator::linkTo available as a simple function * @param string $app app * @param string $file file * @param array $args array with param=>value, will be appended to the returned url * @return string link to the file * * For further information have a look at \OCP\IURLGenerator::linkTo */ function link_to( $app, $file, $args = array() ) { return \OC::$server->getURLGenerator()->linkTo($app, $file, $args); } /** * @param $key * @return string url to the online documentation */ function link_to_docs($key) { return \OC::$server->getURLGenerator()->linkToDocs($key); } /** * make \OCP\IURLGenerator::imagePath available as a simple function * @param string $app app * @param string $image image * @return string link to the image * * For further information have a look at \OCP\IURLGenerator::imagePath */ function image_path( $app, $image ) { return \OC::$server->getURLGenerator()->imagePath( $app, $image ); } /** * make OC_Helper::mimetypeIcon available as a simple function * @param string $mimetype mimetype * @return string link to the image */ function mimetype_icon( $mimetype ) { return \OC::$server->getMimeTypeDetector()->mimeTypeIcon( $mimetype ); } /** * make preview_icon available as a simple function * Returns the path to the preview of the image. * @param string $path path of file * @return link to the preview */ function preview_icon( $path ) { return \OC::$server->getURLGenerator()->linkToRoute('core.Preview.getPreview', ['x' => 32, 'y' => 32, 'file' => $path]); } /** * @param string $path */ function publicPreview_icon ( $path, $token ) { return \OC::$server->getURLGenerator()->linkToRoute('files_sharing.PublicPreview.getPreview', ['x' => 32, 'y' => 32, 'file' => $path, 't' => $token]); } /** * make OC_Helper::humanFileSize available as a simple function * @param int $bytes size in bytes * @return string size as string * * For further information have a look at OC_Helper::humanFileSize */ function human_file_size( $bytes ) { return OC_Helper::humanFileSize( $bytes ); } /** * Strips the timestamp of its time value * @param int $timestamp UNIX timestamp to strip * @return $timestamp without time value */ function strip_time($timestamp){ $date = new \DateTime("@{$timestamp}"); $date->setTime(0, 0, 0); return intval($date->format('U')); } /** * Formats timestamp relatively to the current time using * a human-friendly format like "x minutes ago" or "yesterday" * @param int $timestamp timestamp to format * @param int $fromTime timestamp to compare from, defaults to current time * @param bool $dateOnly whether to strip time information * @return string timestamp */ function relative_modified_date($timestamp, $fromTime = null, $dateOnly = false) { /** @var \OC\DateTimeFormatter $formatter */ $formatter = \OC::$server->query('DateTimeFormatter'); if ($dateOnly){ return $formatter->formatDateSpan($timestamp, $fromTime); } return $formatter->formatTimeSpan($timestamp, $fromTime); } function html_select_options($options, $selected, $params=array()) { if (!is_array($selected)) { $selected=array($selected); } if (isset($params['combine']) && $params['combine']) { $options = array_combine($options, $options); } $value_name = $label_name = false; if (isset($params['value'])) { $value_name = $params['value']; } if (isset($params['label'])) { $label_name = $params['label']; } $html = ''; foreach($options as $value => $label) { if ($value_name && is_array($label)) { $value = $label[$value_name]; } if ($label_name && is_array($label)) { $label = $label[$label_name]; } $select = in_array($value, $selected) ? ' selected="selected"' : ''; $html .= '<option value="' . \OCP\Util::sanitizeHTML($value) . '"' . $select . '>' . \OCP\Util::sanitizeHTML($label) . '</option>'."\n"; } return $html; } private/DatabaseException.php 0000604 00000002204 15247130452 0012307 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class DatabaseException extends \Exception { private $query; //FIXME getQuery seems to be unused, maybe use parent constructor with $message, $code and $previous public function __construct($message, $query = null){ parent::__construct($message); $this->query = $query; } public function getQuery() { return $this->query; } } private/DatabaseSetupException.php 0000604 00000001547 15247130452 0013341 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class DatabaseSetupException extends HintException { } private/NavigationManager.php 0000604 00000017450 15247130452 0012327 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud GmbH * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\App\AppManager; use OC\Group\Manager; use OCP\App\IAppManager; use OCP\IConfig; use OCP\IGroupManager; use OCP\INavigationManager; use OCP\IURLGenerator; use OCP\IUserSession; use OCP\L10N\IFactory; /** * Manages the ownCloud navigation */ class NavigationManager implements INavigationManager { protected $entries = []; protected $closureEntries = []; protected $activeEntry; /** @var bool */ protected $init = false; /** @var IAppManager|AppManager */ protected $appManager; /** @var IURLGenerator */ private $urlGenerator; /** @var IFactory */ private $l10nFac; /** @var IUserSession */ private $userSession; /** @var IGroupManager|Manager */ private $groupManager; /** @var IConfig */ private $config; public function __construct(IAppManager $appManager, IURLGenerator $urlGenerator, IFactory $l10nFac, IUserSession $userSession, IGroupManager $groupManager, IConfig $config) { $this->appManager = $appManager; $this->urlGenerator = $urlGenerator; $this->l10nFac = $l10nFac; $this->userSession = $userSession; $this->groupManager = $groupManager; $this->config = $config; } /** * Creates a new navigation entry * * @param array|\Closure $entry Array containing: id, name, order, icon and href key * The use of a closure is preferred, because it will avoid * loading the routing of your app, unless required. * @return void */ public function add($entry) { if ($entry instanceof \Closure) { $this->closureEntries[] = $entry; return; } $entry['active'] = false; if(!isset($entry['icon'])) { $entry['icon'] = ''; } if(!isset($entry['type'])) { $entry['type'] = 'link'; } $this->entries[] = $entry; } /** * returns all the added Menu entries * @param string $type * @return array an array of the added entries */ public function getAll($type = 'link') { $this->init(); foreach ($this->closureEntries as $c) { $this->add($c()); } $this->closureEntries = array(); if ($type === 'all') { return $this->entries; } return array_filter($this->entries, function($entry) use ($type) { return $entry['type'] === $type; }); } /** * removes all the entries */ public function clear($loadDefaultLinks = true) { $this->entries = []; $this->closureEntries = []; $this->init = !$loadDefaultLinks; } /** * Sets the current navigation entry of the currently running app * @param string $id of the app entry to activate (from added $entry) */ public function setActiveEntry($id) { $this->activeEntry = $id; } /** * gets the active Menu entry * @return string id or empty string * * This function returns the id of the active navigation entry (set by * setActiveEntry */ public function getActiveEntry() { return $this->activeEntry; } private function init() { if ($this->init) { return; } $this->init = true; $l = $this->l10nFac->get('lib'); if ($this->config->getSystemValue('knowledgebaseenabled', true)) { $this->add([ 'type' => 'settings', 'id' => 'help', 'order' => 5, 'href' => $this->urlGenerator->linkToRoute('settings_help'), 'name' => $l->t('Help'), 'icon' => $this->urlGenerator->imagePath('settings', 'help.svg'), ]); } if ($this->userSession->isLoggedIn()) { if ($this->isAdmin()) { // App management $this->add([ 'type' => 'settings', 'id' => 'core_apps', 'order' => 3, 'href' => $this->urlGenerator->linkToRoute('settings.AppSettings.viewApps'), 'icon' => $this->urlGenerator->imagePath('settings', 'apps.svg'), 'name' => $l->t('Apps'), ]); } // Personal settings $this->add([ 'type' => 'settings', 'id' => 'personal', 'order' => 1, 'href' => $this->urlGenerator->linkToRoute('settings_personal'), 'name' => $l->t('Personal'), 'icon' => $this->urlGenerator->imagePath('settings', 'personal.svg'), ]); $logoutUrl = \OC_User::getLogoutUrl($this->urlGenerator); if($logoutUrl !== '') { // Logout $this->add([ 'type' => 'settings', 'id' => 'logout', 'order' => 99999, 'href' => $logoutUrl, 'name' => $l->t('Log out'), 'icon' => $this->urlGenerator->imagePath('core', 'actions/logout.svg'), ]); } if ($this->isSubadmin()) { // User management $this->add([ 'type' => 'settings', 'id' => 'core_users', 'order' => 4, 'href' => $this->urlGenerator->linkToRoute('settings_users'), 'name' => $l->t('Users'), 'icon' => $this->urlGenerator->imagePath('settings', 'users.svg'), ]); } if ($this->isAdmin()) { // Admin settings $this->add([ 'type' => 'settings', 'id' => 'admin', 'order' => 2, 'href' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index'), 'name' => $l->t('Admin'), 'icon' => $this->urlGenerator->imagePath('settings', 'admin.svg'), ]); } } if ($this->appManager === 'null') { return; } if ($this->userSession->isLoggedIn()) { $apps = $this->appManager->getEnabledAppsForUser($this->userSession->getUser()); } else { $apps = $this->appManager->getInstalledApps(); } foreach ($apps as $app) { if (!$this->userSession->isLoggedIn() && !$this->appManager->isEnabledForUser($app, $this->userSession->getUser())) { continue; } // load plugins and collections from info.xml $info = $this->appManager->getAppInfo($app); if (empty($info['navigations'])) { continue; } foreach ($info['navigations'] as $nav) { if (!isset($nav['name'])) { continue; } if (!isset($nav['route'])) { continue; } $role = isset($nav['@attributes']['role']) ? $nav['@attributes']['role'] : 'all'; if ($role === 'admin' && !$this->isAdmin()) { continue; } $l = $this->l10nFac->get($app); $id = isset($nav['id']) ? $nav['id'] : $app; $order = isset($nav['order']) ? $nav['order'] : 100; $type = isset($nav['type']) ? $nav['type'] : 'link'; $route = $this->urlGenerator->linkToRoute($nav['route']); $icon = isset($nav['icon']) ? $nav['icon'] : 'app.svg'; foreach ([$icon, "$app.svg"] as $i) { try { $icon = $this->urlGenerator->imagePath($app, $i); break; } catch (\RuntimeException $ex) { // no icon? - ignore it then } } if ($icon === null) { $icon = $this->urlGenerator->imagePath('core', 'default-app-icon'); } $this->add([ 'id' => $id, 'order' => $order, 'href' => $route, 'icon' => $icon, 'type' => $type, 'name' => $l->t($nav['name']), ]); } } } private function isAdmin() { $user = $this->userSession->getUser(); if ($user !== null) { return $this->groupManager->isAdmin($user->getUID()); } return false; } private function isSubadmin() { $user = $this->userSession->getUser(); if ($user !== null) { return $this->groupManager->getSubAdmin()->isSubAdmin($user); } return false; } } private/Setup.php 0000604 00000040712 15247130452 0010032 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Administrator <Administrator@WINDOWS-2012> * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Brice Maron <brice@bmaron.net> * @author Christoph Wurst <christoph@owncloud.com> * @author François Kubler <francois@kubler.org> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Martin Mattel <martin.mattel@diemattels.at> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sean Comeau <sean@ftlnetworks.ca> * @author Serge Martin <edb@sigluy.net> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; use Exception; use OC\App\AppStore\Bundles\BundleFetcher; use OCP\Defaults; use OCP\IL10N; use OCP\ILogger; use OCP\Security\ISecureRandom; class Setup { /** @var SystemConfig */ protected $config; /** @var IniGetWrapper */ protected $iniWrapper; /** @var IL10N */ protected $l10n; /** @var Defaults */ protected $defaults; /** @var ILogger */ protected $logger; /** @var ISecureRandom */ protected $random; /** * @param SystemConfig $config * @param IniGetWrapper $iniWrapper * @param IL10N $l10n * @param Defaults $defaults * @param ILogger $logger * @param ISecureRandom $random */ public function __construct(SystemConfig $config, IniGetWrapper $iniWrapper, IL10N $l10n, Defaults $defaults, ILogger $logger, ISecureRandom $random ) { $this->config = $config; $this->iniWrapper = $iniWrapper; $this->l10n = $l10n; $this->defaults = $defaults; $this->logger = $logger; $this->random = $random; } static $dbSetupClasses = [ 'mysql' => \OC\Setup\MySQL::class, 'pgsql' => \OC\Setup\PostgreSQL::class, 'oci' => \OC\Setup\OCI::class, 'sqlite' => \OC\Setup\Sqlite::class, 'sqlite3' => \OC\Setup\Sqlite::class, ]; /** * Wrapper around the "class_exists" PHP function to be able to mock it * @param string $name * @return bool */ protected function class_exists($name) { return class_exists($name); } /** * Wrapper around the "is_callable" PHP function to be able to mock it * @param string $name * @return bool */ protected function is_callable($name) { return is_callable($name); } /** * Wrapper around \PDO::getAvailableDrivers * * @return array */ protected function getAvailableDbDriversForPdo() { return \PDO::getAvailableDrivers(); } /** * Get the available and supported databases of this instance * * @param bool $allowAllDatabases * @return array * @throws Exception */ public function getSupportedDatabases($allowAllDatabases = false) { $availableDatabases = array( 'sqlite' => array( 'type' => 'pdo', 'call' => 'sqlite', 'name' => 'SQLite' ), 'mysql' => array( 'type' => 'pdo', 'call' => 'mysql', 'name' => 'MySQL/MariaDB' ), 'pgsql' => array( 'type' => 'pdo', 'call' => 'pgsql', 'name' => 'PostgreSQL' ), 'oci' => array( 'type' => 'function', 'call' => 'oci_connect', 'name' => 'Oracle' ) ); if ($allowAllDatabases) { $configuredDatabases = array_keys($availableDatabases); } else { $configuredDatabases = $this->config->getValue('supportedDatabases', array('sqlite', 'mysql', 'pgsql')); } if(!is_array($configuredDatabases)) { throw new Exception('Supported databases are not properly configured.'); } $supportedDatabases = array(); foreach($configuredDatabases as $database) { if(array_key_exists($database, $availableDatabases)) { $working = false; $type = $availableDatabases[$database]['type']; $call = $availableDatabases[$database]['call']; if ($type === 'function') { $working = $this->is_callable($call); } elseif($type === 'pdo') { $working = in_array($call, $this->getAvailableDbDriversForPdo(), TRUE); } if($working) { $supportedDatabases[$database] = $availableDatabases[$database]['name']; } } } return $supportedDatabases; } /** * Gathers system information like database type and does * a few system checks. * * @return array of system info, including an "errors" value * in case of errors/warnings */ public function getSystemInfo($allowAllDatabases = false) { $databases = $this->getSupportedDatabases($allowAllDatabases); $dataDir = $this->config->getValue('datadirectory', \OC::$SERVERROOT.'/data'); $errors = array(); // Create data directory to test whether the .htaccess works // Notice that this is not necessarily the same data directory as the one // that will effectively be used. if(!file_exists($dataDir)) { @mkdir($dataDir); } $htAccessWorking = true; if (is_dir($dataDir) && is_writable($dataDir)) { // Protect data directory here, so we can test if the protection is working \OC\Setup::protectDataDirectory(); try { $util = new \OC_Util(); $htAccessWorking = $util->isHtaccessWorking(\OC::$server->getConfig()); } catch (\OC\HintException $e) { $errors[] = array( 'error' => $e->getMessage(), 'hint' => $e->getHint() ); $htAccessWorking = false; } } if (\OC_Util::runningOnMac()) { $errors[] = array( 'error' => $this->l10n->t( 'Mac OS X is not supported and %s will not work properly on this platform. ' . 'Use it at your own risk! ', $this->defaults->getName() ), 'hint' => $this->l10n->t('For the best results, please consider using a GNU/Linux server instead.') ); } if($this->iniWrapper->getString('open_basedir') !== '' && PHP_INT_SIZE === 4) { $errors[] = array( 'error' => $this->l10n->t( 'It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. ' . 'This will lead to problems with files over 4 GB and is highly discouraged.', $this->defaults->getName() ), 'hint' => $this->l10n->t('Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP.') ); } return array( 'hasSQLite' => isset($databases['sqlite']), 'hasMySQL' => isset($databases['mysql']), 'hasPostgreSQL' => isset($databases['pgsql']), 'hasOracle' => isset($databases['oci']), 'databases' => $databases, 'directory' => $dataDir, 'htaccessWorking' => $htAccessWorking, 'errors' => $errors, ); } /** * @param $options * @return array */ public function install($options) { $l = $this->l10n; $error = array(); $dbType = $options['dbtype']; if(empty($options['adminlogin'])) { $error[] = $l->t('Set an admin username.'); } if(empty($options['adminpass'])) { $error[] = $l->t('Set an admin password.'); } if(empty($options['directory'])) { $options['directory'] = \OC::$SERVERROOT."/data"; } if (!isset(self::$dbSetupClasses[$dbType])) { $dbType = 'sqlite'; } $username = htmlspecialchars_decode($options['adminlogin']); $password = htmlspecialchars_decode($options['adminpass']); $dataDir = htmlspecialchars_decode($options['directory']); $class = self::$dbSetupClasses[$dbType]; /** @var \OC\Setup\AbstractDatabase $dbSetup */ $dbSetup = new $class($l, 'db_structure.xml', $this->config, $this->logger, $this->random); $error = array_merge($error, $dbSetup->validate($options)); // validate the data directory if ( (!is_dir($dataDir) and !mkdir($dataDir)) or !is_writable($dataDir) ) { $error[] = $l->t("Can't create or write into the data directory %s", array($dataDir)); } if(count($error) != 0) { return $error; } $request = \OC::$server->getRequest(); //no errors, good if(isset($options['trusted_domains']) && is_array($options['trusted_domains'])) { $trustedDomains = $options['trusted_domains']; } else { $trustedDomains = [$request->getInsecureServerHost()]; } //use sqlite3 when available, otherwise sqlite2 will be used. if($dbType=='sqlite' and class_exists('SQLite3')) { $dbType='sqlite3'; } //generate a random salt that is used to salt the local user passwords $salt = $this->random->generate(30); // generate a secret $secret = $this->random->generate(48); //write the config file $this->config->setValues([ 'passwordsalt' => $salt, 'secret' => $secret, 'trusted_domains' => $trustedDomains, 'datadirectory' => $dataDir, 'overwrite.cli.url' => $request->getServerProtocol() . '://' . $request->getInsecureServerHost() . \OC::$WEBROOT, 'dbtype' => $dbType, 'version' => implode('.', \OCP\Util::getVersion()), ]); try { $dbSetup->initialize($options); $dbSetup->setupDatabase($username); } catch (\OC\DatabaseSetupException $e) { $error[] = array( 'error' => $e->getMessage(), 'hint' => $e->getHint() ); return($error); } catch (Exception $e) { $error[] = array( 'error' => 'Error while trying to create admin user: ' . $e->getMessage(), 'hint' => '' ); return($error); } //create the user and group $user = null; try { $user = \OC::$server->getUserManager()->createUser($username, $password); if (!$user) { $error[] = "User <$username> could not be created."; } } catch(Exception $exception) { $error[] = $exception->getMessage(); } if(count($error) == 0) { $config = \OC::$server->getConfig(); $config->setAppValue('core', 'installedat', microtime(true)); $config->setAppValue('core', 'lastupdatedat', microtime(true)); $config->setAppValue('core', 'vendor', $this->getVendor()); $group =\OC::$server->getGroupManager()->createGroup('admin'); $group->addUser($user); // Install shipped apps and specified app bundles Installer::installShippedApps(); $installer = new Installer( \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), \OC::$server->getLogger(), \OC::$server->getConfig() ); $bundleFetcher = new BundleFetcher(\OC::$server->getL10N('lib')); $defaultInstallationBundles = $bundleFetcher->getDefaultInstallationBundle(); foreach($defaultInstallationBundles as $bundle) { try { $installer->installAppBundle($bundle); } catch (Exception $e) {} } // create empty file in data dir, so we can later find // out that this is indeed an ownCloud data directory file_put_contents($config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').'/.ocdata', ''); // Update .htaccess files Setup::updateHtaccess(); Setup::protectDataDirectory(); self::installBackgroundJobs(); //and we are done $config->setSystemValue('installed', true); // Create a session token for the newly created user // The token provider requires a working db, so it's not injected on setup /* @var $userSession User\Session */ $userSession = \OC::$server->getUserSession(); $defaultTokenProvider = \OC::$server->query('OC\Authentication\Token\DefaultTokenProvider'); $userSession->setTokenProvider($defaultTokenProvider); $userSession->login($username, $password); $userSession->createSessionToken($request, $userSession->getUser()->getUID(), $username, $password); } return $error; } public static function installBackgroundJobs() { \OC::$server->getJobList()->add('\OC\Authentication\Token\DefaultTokenCleanupJob'); } /** * @return string Absolute path to htaccess */ private function pathToHtaccess() { return \OC::$SERVERROOT.'/.htaccess'; } /** * Append the correct ErrorDocument path for Apache hosts * @return bool True when success, False otherwise */ public static function updateHtaccess() { $config = \OC::$server->getSystemConfig(); // For CLI read the value from overwrite.cli.url if(\OC::$CLI) { $webRoot = $config->getValue('overwrite.cli.url', ''); if($webRoot === '') { return false; } $webRoot = parse_url($webRoot, PHP_URL_PATH); $webRoot = rtrim($webRoot, '/'); } else { $webRoot = !empty(\OC::$WEBROOT) ? \OC::$WEBROOT : '/'; } $setupHelper = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'), \OC::$server->query(Defaults::class), \OC::$server->getLogger(), \OC::$server->getSecureRandom()); $htaccessContent = file_get_contents($setupHelper->pathToHtaccess()); $content = "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####\n"; $htaccessContent = explode($content, $htaccessContent, 2)[0]; //custom 403 error page $content.= "\nErrorDocument 403 ".$webRoot."/core/templates/403.php"; //custom 404 error page $content.= "\nErrorDocument 404 ".$webRoot."/core/templates/404.php"; // Add rewrite rules if the RewriteBase is configured $rewriteBase = $config->getValue('htaccess.RewriteBase', ''); if($rewriteBase !== '') { $content .= "\n<IfModule mod_rewrite.c>"; $content .= "\n Options -MultiViews"; $content .= "\n RewriteRule ^core/js/oc.js$ index.php [PT,E=PATH_INFO:$1]"; $content .= "\n RewriteRule ^core/preview.png$ index.php [PT,E=PATH_INFO:$1]"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !\\.(css|js|svg|gif|png|html|ttf|woff|ico|jpg|jpeg)$"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !core/img/favicon.ico$"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !core/img/manifest.json$"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/remote.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/public.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/cron.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/core/ajax/update.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/status.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/ocs/v1.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/ocs/v2.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/robots.txt"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/updater/"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/ocs-provider/"; $content .= "\n RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/.*"; $content .= "\n RewriteRule . index.php [PT,E=PATH_INFO:$1]"; $content .= "\n RewriteBase " . $rewriteBase; $content .= "\n <IfModule mod_env.c>"; $content .= "\n SetEnv front_controller_active true"; $content .= "\n <IfModule mod_dir.c>"; $content .= "\n DirectorySlash off"; $content .= "\n </IfModule>"; $content .= "\n </IfModule>"; $content .= "\n</IfModule>"; } if ($content !== '') { //suppress errors in case we don't have permissions for it return (bool) @file_put_contents($setupHelper->pathToHtaccess(), $htaccessContent.$content . "\n"); } return false; } public static function protectDataDirectory() { //Require all denied $now = date('Y-m-d H:i:s'); $content = "# Generated by Nextcloud on $now\n"; $content.= "# line below if for Apache 2.4\n"; $content.= "<ifModule mod_authz_core.c>\n"; $content.= "Require all denied\n"; $content.= "</ifModule>\n\n"; $content.= "# line below if for Apache 2.2\n"; $content.= "<ifModule !mod_authz_core.c>\n"; $content.= "deny from all\n"; $content.= "Satisfy All\n"; $content.= "</ifModule>\n\n"; $content.= "# section for Apache 2.2 and 2.4\n"; $content.= "<ifModule mod_autoindex.c>\n"; $content.= "IndexIgnore *\n"; $content.= "</ifModule>\n"; $baseDir = \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data'); file_put_contents($baseDir . '/.htaccess', $content); file_put_contents($baseDir . '/index.html', ''); } /** * Return vendor from which this version was published * * @return string Get the vendor * * Copy of \OC\Updater::getVendor() */ private function getVendor() { // this should really be a JSON file require \OC::$SERVERROOT . '/version.php'; /** @var string $vendor */ return (string) $vendor; } } private/Updater.php 0000604 00000055522 15247130452 0010343 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Frank Karlitschek <frank@karlitschek.de> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Steffen Lindner <mail@steffen-lindner.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\Hooks\BasicEmitter; use OC\IntegrityCheck\Checker; use OC_App; use OCP\IConfig; use OCP\ILogger; use OCP\Util; use Symfony\Component\EventDispatcher\GenericEvent; /** * Class that handles autoupdating of ownCloud * * Hooks provided in scope \OC\Updater * - maintenanceStart() * - maintenanceEnd() * - dbUpgrade() * - failure(string $message) */ class Updater extends BasicEmitter { /** @var ILogger $log */ private $log; /** @var IConfig */ private $config; /** @var Checker */ private $checker; /** @var bool */ private $skip3rdPartyAppsDisable; private $logLevelNames = [ 0 => 'Debug', 1 => 'Info', 2 => 'Warning', 3 => 'Error', 4 => 'Fatal', ]; /** * @param IConfig $config * @param Checker $checker * @param ILogger $log */ public function __construct(IConfig $config, Checker $checker, ILogger $log = null) { $this->log = $log; $this->config = $config; $this->checker = $checker; // If at least PHP 7.0.0 is used we don't need to disable apps as we catch // fatal errors and exceptions and disable the app just instead. if(version_compare(phpversion(), '7.0.0', '>=')) { $this->skip3rdPartyAppsDisable = true; } } /** * Sets whether the update disables 3rd party apps. * This can be set to true to skip the disable. * * @param bool $flag false to not disable, true otherwise */ public function setSkip3rdPartyAppsDisable($flag) { $this->skip3rdPartyAppsDisable = $flag; } /** * runs the update actions in maintenance mode, does not upgrade the source files * except the main .htaccess file * * @return bool true if the operation succeeded, false otherwise */ public function upgrade() { $this->emitRepairEvents(); $this->logAllEvents(); $logLevel = $this->config->getSystemValue('loglevel', Util::WARN); $this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); $this->config->setSystemValue('loglevel', Util::DEBUG); $wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false); if(!$wasMaintenanceModeEnabled) { $this->config->setSystemValue('maintenance', true); $this->emit('\OC\Updater', 'maintenanceEnabled'); } $installedVersion = $this->config->getSystemValue('version', '0.0.0'); $currentVersion = implode('.', \OCP\Util::getVersion()); $this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core')); $success = true; try { $this->doUpgrade($currentVersion, $installedVersion); } catch (HintException $exception) { $this->log->logException($exception, ['app' => 'core']); $this->emit('\OC\Updater', 'failure', array($exception->getMessage() . ': ' .$exception->getHint())); $success = false; } catch (\Exception $exception) { $this->log->logException($exception, ['app' => 'core']); $this->emit('\OC\Updater', 'failure', array(get_class($exception) . ': ' .$exception->getMessage())); $success = false; } $this->emit('\OC\Updater', 'updateEnd', array($success)); if(!$wasMaintenanceModeEnabled && $success) { $this->config->setSystemValue('maintenance', false); $this->emit('\OC\Updater', 'maintenanceDisabled'); } else { $this->emit('\OC\Updater', 'maintenanceActive'); } $this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); $this->config->setSystemValue('loglevel', $logLevel); $this->config->setSystemValue('installed', true); return $success; } /** * Return version from which this version is allowed to upgrade from * * @return array allowed previous versions per vendor */ private function getAllowedPreviousVersions() { // this should really be a JSON file require \OC::$SERVERROOT . '/version.php'; /** @var array $OC_VersionCanBeUpgradedFrom */ return $OC_VersionCanBeUpgradedFrom; } /** * Return vendor from which this version was published * * @return string Get the vendor */ private function getVendor() { // this should really be a JSON file require \OC::$SERVERROOT . '/version.php'; /** @var string $vendor */ return (string) $vendor; } /** * Whether an upgrade to a specified version is possible * @param string $oldVersion * @param string $newVersion * @param array $allowedPreviousVersions * @return bool */ public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) { $version = explode('.', $oldVersion); $majorMinor = $version[0] . '.' . $version[1]; $currentVendor = $this->config->getAppValue('core', 'vendor', ''); // Vendor was not set correctly on install, so we have to white-list known versions if ($currentVendor === '') { if (in_array($oldVersion, [ '11.0.2.7', '11.0.1.2', '11.0.0.10', ], true)) { $currentVendor = 'nextcloud'; } else if (isset($allowedPreviousVersions['owncloud'][$oldVersion])) { $currentVendor = 'owncloud'; } } if ($currentVendor === 'nextcloud') { return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) && (version_compare($oldVersion, $newVersion, '<=') || $this->config->getSystemValue('debug', false)); } // Check if the instance can be migrated return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) || isset($allowedPreviousVersions[$currentVendor][$oldVersion]); } /** * runs the update actions in maintenance mode, does not upgrade the source files * except the main .htaccess file * * @param string $currentVersion current version to upgrade to * @param string $installedVersion previous version from which to upgrade from * * @throws \Exception */ private function doUpgrade($currentVersion, $installedVersion) { // Stop update if the update is over several major versions $allowedPreviousVersions = $this->getAllowedPreviousVersions(); if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) { throw new \Exception('Updates between multiple major versions and downgrades are unsupported.'); } // Update .htaccess files try { Setup::updateHtaccess(); Setup::protectDataDirectory(); } catch (\Exception $e) { throw new \Exception($e->getMessage()); } // create empty file in data dir, so we can later find // out that this is indeed an ownCloud data directory // (in case it didn't exist before) file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', ''); // pre-upgrade repairs $repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher()); $repair->run(); $this->doCoreUpgrade(); try { // TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378 Setup::installBackgroundJobs(); } catch (\Exception $e) { throw new \Exception($e->getMessage()); } // update all shipped apps $this->checkAppsRequirements(); $this->doAppUpgrade(); // Update the appfetchers version so it downloads the correct list from the appstore \OC::$server->getAppFetcher()->setVersion($currentVersion); // upgrade appstore apps $this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps()); // install new shipped apps on upgrade OC_App::loadApps('authentication'); $errors = Installer::installShippedApps(true); foreach ($errors as $appId => $exception) { /** @var \Exception $exception */ $this->log->logException($exception, ['app' => $appId]); $this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]); } // post-upgrade repairs $repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher()); $repair->run(); //Invalidate update feed $this->config->setAppValue('core', 'lastupdatedat', 0); // Check for code integrity if not disabled if(\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) { $this->emit('\OC\Updater', 'startCheckCodeIntegrity'); $this->checker->runInstanceVerification(); $this->emit('\OC\Updater', 'finishedCheckCodeIntegrity'); } // only set the final version if everything went well $this->config->setSystemValue('version', implode('.', Util::getVersion())); $this->config->setAppValue('core', 'vendor', $this->getVendor()); } protected function doCoreUpgrade() { $this->emit('\OC\Updater', 'dbUpgradeBefore'); // do the real upgrade \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml'); $this->emit('\OC\Updater', 'dbUpgrade'); } /** * @param string $version the oc version to check app compatibility with */ protected function checkAppUpgrade($version) { $apps = \OC_App::getEnabledApps(); $this->emit('\OC\Updater', 'appUpgradeCheckBefore'); foreach ($apps as $appId) { $info = \OC_App::getAppInfo($appId); $compatible = \OC_App::isAppCompatible($version, $info); $isShipped = \OC_App::isShipped($appId); if ($compatible && $isShipped && \OC_App::shouldUpgrade($appId)) { /** * FIXME: The preupdate check is performed before the database migration, otherwise database changes * are not possible anymore within it. - Consider this when touching the code. * @link https://github.com/owncloud/core/issues/10980 * @see \OC_App::updateApp */ if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/preupdate.php')) { $this->includePreUpdate($appId); } if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) { $this->emit('\OC\Updater', 'appSimulateUpdate', array($appId)); \OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml'); } } } $this->emit('\OC\Updater', 'appUpgradeCheck'); } /** * Includes the pre-update file. Done here to prevent namespace mixups. * @param string $appId */ private function includePreUpdate($appId) { include \OC_App::getAppPath($appId) . '/appinfo/preupdate.php'; } /** * upgrades all apps within a major ownCloud upgrade. Also loads "priority" * (types authentication, filesystem, logging, in that order) afterwards. * * @throws NeedsUpdateException */ protected function doAppUpgrade() { $apps = \OC_App::getEnabledApps(); $priorityTypes = array('authentication', 'filesystem', 'logging'); $pseudoOtherType = 'other'; $stacks = array($pseudoOtherType => array()); foreach ($apps as $appId) { $priorityType = false; foreach ($priorityTypes as $type) { if(!isset($stacks[$type])) { $stacks[$type] = array(); } if (\OC_App::isType($appId, $type)) { $stacks[$type][] = $appId; $priorityType = true; break; } } if (!$priorityType) { $stacks[$pseudoOtherType][] = $appId; } } foreach ($stacks as $type => $stack) { foreach ($stack as $appId) { if (\OC_App::shouldUpgrade($appId)) { $this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]); \OC_App::updateApp($appId); $this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]); } if($type !== $pseudoOtherType) { // load authentication, filesystem and logging apps after // upgrading them. Other apps my need to rely on modifying // user and/or filesystem aspects. \OC_App::loadApp($appId); } } } } /** * check if the current enabled apps are compatible with the current * ownCloud version. disable them if not. * This is important if you upgrade ownCloud and have non ported 3rd * party apps installed. * * @return array * @throws \Exception */ private function checkAppsRequirements() { $isCoreUpgrade = $this->isCodeUpgrade(); $apps = OC_App::getEnabledApps(); $version = Util::getVersion(); $disabledApps = []; foreach ($apps as $app) { // check if the app is compatible with this version of ownCloud $info = OC_App::getAppInfo($app); if(!OC_App::isAppCompatible($version, $info)) { if (OC_App::isShipped($app)) { throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update'); } OC_App::disable($app); $this->emit('\OC\Updater', 'incompatibleAppDisabled', array($app)); } // no need to disable any app in case this is a non-core upgrade if (!$isCoreUpgrade) { continue; } // shipped apps will remain enabled if (OC_App::isShipped($app)) { continue; } // authentication and session apps will remain enabled as well if (OC_App::isType($app, ['session', 'authentication'])) { continue; } // disable any other 3rd party apps if not overriden if(!$this->skip3rdPartyAppsDisable) { \OC_App::disable($app); $disabledApps[]= $app; $this->emit('\OC\Updater', 'thirdPartyAppDisabled', array($app)); }; } return $disabledApps; } /** * @return bool */ private function isCodeUpgrade() { $installedVersion = $this->config->getSystemValue('version', '0.0.0'); $currentVersion = implode('.', Util::getVersion()); if (version_compare($currentVersion, $installedVersion, '>')) { return true; } return false; } /** * @param array $disabledApps * @throws \Exception */ private function upgradeAppStoreApps(array $disabledApps) { foreach($disabledApps as $app) { try { $installer = new Installer( \OC::$server->getAppFetcher(), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), $this->log, \OC::$server->getConfig() ); $this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]); if (Installer::isUpdateAvailable($app, \OC::$server->getAppFetcher())) { $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]); $installer->updateAppstoreApp($app); } $this->emit('\OC\Updater', 'checkAppStoreApp', [$app]); } catch (\Exception $ex) { $this->log->logException($ex, ['app' => 'core']); } } } /** * Forward messages emitted by the repair routine */ private function emitRepairEvents() { $dispatcher = \OC::$server->getEventDispatcher(); $dispatcher->addListener('\OC\Repair::warning', function ($event) { if ($event instanceof GenericEvent) { $this->emit('\OC\Updater', 'repairWarning', $event->getArguments()); } }); $dispatcher->addListener('\OC\Repair::error', function ($event) { if ($event instanceof GenericEvent) { $this->emit('\OC\Updater', 'repairError', $event->getArguments()); } }); $dispatcher->addListener('\OC\Repair::info', function ($event) { if ($event instanceof GenericEvent) { $this->emit('\OC\Updater', 'repairInfo', $event->getArguments()); } }); $dispatcher->addListener('\OC\Repair::step', function ($event) { if ($event instanceof GenericEvent) { $this->emit('\OC\Updater', 'repairStep', $event->getArguments()); } }); } private function logAllEvents() { $log = $this->log; $dispatcher = \OC::$server->getEventDispatcher(); $dispatcher->addListener('\OC\DB\Migrator::executeSql', function($event) use ($log) { if (!$event instanceof GenericEvent) { return; } $log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']); }); $dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($log) { if (!$event instanceof GenericEvent) { return; } $log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']); }); $repairListener = function($event) use ($log) { if (!$event instanceof GenericEvent) { return; } switch ($event->getSubject()) { case '\OC\Repair::startProgress': $log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']); break; case '\OC\Repair::advance': $desc = $event->getArgument(1); if (empty($desc)) { $desc = ''; } $log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']); break; case '\OC\Repair::finishProgress': $log->info('\OC\Repair::finishProgress', ['app' => 'updater']); break; case '\OC\Repair::step': $log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']); break; case '\OC\Repair::info': $log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']); break; case '\OC\Repair::warning': $log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']); break; case '\OC\Repair::error': $log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']); break; } }; $dispatcher->addListener('\OC\Repair::startProgress', $repairListener); $dispatcher->addListener('\OC\Repair::advance', $repairListener); $dispatcher->addListener('\OC\Repair::finishProgress', $repairListener); $dispatcher->addListener('\OC\Repair::step', $repairListener); $dispatcher->addListener('\OC\Repair::info', $repairListener); $dispatcher->addListener('\OC\Repair::warning', $repairListener); $dispatcher->addListener('\OC\Repair::error', $repairListener); $this->listen('\OC\Updater', 'maintenanceEnabled', function () use($log) { $log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'maintenanceDisabled', function () use($log) { $log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'maintenanceActive', function () use($log) { $log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'updateEnd', function ($success) use($log) { if ($success) { $log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']); } else { $log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']); } }); $this->listen('\OC\Updater', 'dbUpgradeBefore', function () use($log) { $log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'dbUpgrade', function () use($log) { $log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($log) { $log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($log) { $log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($log) { $log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']); }); $this->listen('\OC\Updater', 'thirdPartyAppDisabled', function ($app) use ($log) { $log->info('\OC\Updater::thirdPartyAppDisabled: Disabled 3rd-party app: ' . $app, ['app' => 'updater']); }); $this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use($log) { $log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($log) { $log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use($log) { $log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) { $log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) { $log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) { $log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) { $log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) { $log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']); }); $this->listen('\OC\Updater', 'failure', function ($message) use($log) { $log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']); }); $this->listen('\OC\Updater', 'setDebugLogLevel', function () use($log) { $log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($log) { $log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($log) { $log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']); }); $this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($log) { $log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']); }); } } private/AppFramework/Http.php 0000604 00000014215 15247130452 0012246 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework; use OCP\AppFramework\Http as BaseHttp; class Http extends BaseHttp { private $server; private $protocolVersion; protected $headers; /** * @param array $server $_SERVER * @param string $protocolVersion the http version to use defaults to HTTP/1.1 */ public function __construct($server, $protocolVersion='HTTP/1.1') { $this->server = $server; $this->protocolVersion = $protocolVersion; $this->headers = array( self::STATUS_CONTINUE => 'Continue', self::STATUS_SWITCHING_PROTOCOLS => 'Switching Protocols', self::STATUS_PROCESSING => 'Processing', self::STATUS_OK => 'OK', self::STATUS_CREATED => 'Created', self::STATUS_ACCEPTED => 'Accepted', self::STATUS_NON_AUTHORATIVE_INFORMATION => 'Non-Authorative Information', self::STATUS_NO_CONTENT => 'No Content', self::STATUS_RESET_CONTENT => 'Reset Content', self::STATUS_PARTIAL_CONTENT => 'Partial Content', self::STATUS_MULTI_STATUS => 'Multi-Status', // RFC 4918 self::STATUS_ALREADY_REPORTED => 'Already Reported', // RFC 5842 self::STATUS_IM_USED => 'IM Used', // RFC 3229 self::STATUS_MULTIPLE_CHOICES => 'Multiple Choices', self::STATUS_MOVED_PERMANENTLY => 'Moved Permanently', self::STATUS_FOUND => 'Found', self::STATUS_SEE_OTHER => 'See Other', self::STATUS_NOT_MODIFIED => 'Not Modified', self::STATUS_USE_PROXY => 'Use Proxy', self::STATUS_RESERVED => 'Reserved', self::STATUS_TEMPORARY_REDIRECT => 'Temporary Redirect', self::STATUS_BAD_REQUEST => 'Bad request', self::STATUS_UNAUTHORIZED => 'Unauthorized', self::STATUS_PAYMENT_REQUIRED => 'Payment Required', self::STATUS_FORBIDDEN => 'Forbidden', self::STATUS_NOT_FOUND => 'Not Found', self::STATUS_METHOD_NOT_ALLOWED => 'Method Not Allowed', self::STATUS_NOT_ACCEPTABLE => 'Not Acceptable', self::STATUS_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', self::STATUS_REQUEST_TIMEOUT => 'Request Timeout', self::STATUS_CONFLICT => 'Conflict', self::STATUS_GONE => 'Gone', self::STATUS_LENGTH_REQUIRED => 'Length Required', self::STATUS_PRECONDITION_FAILED => 'Precondition failed', self::STATUS_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large', self::STATUS_REQUEST_URI_TOO_LONG => 'Request-URI Too Long', self::STATUS_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', self::STATUS_REQUEST_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', self::STATUS_EXPECTATION_FAILED => 'Expectation Failed', self::STATUS_IM_A_TEAPOT => 'I\'m a teapot', // RFC 2324 self::STATUS_UNPROCESSABLE_ENTITY => 'Unprocessable Entity', // RFC 4918 self::STATUS_LOCKED => 'Locked', // RFC 4918 self::STATUS_FAILED_DEPENDENCY => 'Failed Dependency', // RFC 4918 self::STATUS_UPGRADE_REQUIRED => 'Upgrade required', self::STATUS_PRECONDITION_REQUIRED => 'Precondition required', // draft-nottingham-http-new-status self::STATUS_TOO_MANY_REQUESTS => 'Too Many Requests', // draft-nottingham-http-new-status self::STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large', // draft-nottingham-http-new-status self::STATUS_INTERNAL_SERVER_ERROR => 'Internal Server Error', self::STATUS_NOT_IMPLEMENTED => 'Not Implemented', self::STATUS_BAD_GATEWAY => 'Bad Gateway', self::STATUS_SERVICE_UNAVAILABLE => 'Service Unavailable', self::STATUS_GATEWAY_TIMEOUT => 'Gateway Timeout', self::STATUS_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version not supported', self::STATUS_VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates', self::STATUS_INSUFFICIENT_STORAGE => 'Insufficient Storage', // RFC 4918 self::STATUS_LOOP_DETECTED => 'Loop Detected', // RFC 5842 self::STATUS_BANDWIDTH_LIMIT_EXCEEDED => 'Bandwidth Limit Exceeded', // non-standard self::STATUS_NOT_EXTENDED => 'Not extended', self::STATUS_NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required', // draft-nottingham-http-new-status ); } /** * Gets the correct header * @param Http::CONSTANT $status the constant from the Http class * @param \DateTime $lastModified formatted last modified date * @param string $ETag the etag * @return string */ public function getStatusHeader($status, \DateTime $lastModified=null, $ETag=null) { if(!is_null($lastModified)) { $lastModified = $lastModified->format(\DateTime::RFC2822); } // if etag or lastmodified have not changed, return a not modified if ((isset($this->server['HTTP_IF_NONE_MATCH']) && trim(trim($this->server['HTTP_IF_NONE_MATCH']), '"') === (string)$ETag) || (isset($this->server['HTTP_IF_MODIFIED_SINCE']) && trim($this->server['HTTP_IF_MODIFIED_SINCE']) === $lastModified)) { $status = self::STATUS_NOT_MODIFIED; } // we have one change currently for the http 1.0 header that differs // from 1.1: STATUS_TEMPORARY_REDIRECT should be STATUS_FOUND // if this differs any more, we want to create childclasses for this if($status === self::STATUS_TEMPORARY_REDIRECT && $this->protocolVersion === 'HTTP/1.0') { $status = self::STATUS_FOUND; } return $this->protocolVersion . ' ' . $status . ' ' . $this->headers[$status]; } } private/AppFramework/Http/Request.php 0000604 00000061134 15247130452 0013700 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Mitar <mitar.git@tnode.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Http; use OC\Security\CSRF\CsrfToken; use OC\Security\CSRF\CsrfTokenManager; use OC\Security\TrustedDomainHelper; use OCP\IConfig; use OCP\IRequest; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; /** * Class for accessing variables in the request. * This class provides an immutable object with request variables. * * @property mixed[] cookies * @property mixed[] env * @property mixed[] files * @property string method * @property mixed[] parameters * @property mixed[] server */ class Request implements \ArrayAccess, \Countable, IRequest { const USER_AGENT_IE = '/(MSIE)|(Trident)/'; // Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/'; // Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/'; // Chrome User Agent from https://developer.chrome.com/multidevice/user-agent const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+$/'; // Safari User Agent from http://www.useragentstring.com/pages/Safari/ const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/'; // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#'; const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#'; const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|::1)$/'; /** * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_IOS instead */ const USER_AGENT_OWNCLOUD_IOS = '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/'; /** * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_ANDROID instead */ const USER_AGENT_OWNCLOUD_ANDROID = '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/'; /** * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_DESKTOP instead */ const USER_AGENT_OWNCLOUD_DESKTOP = '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/'; protected $inputStream; protected $content; protected $items = array(); protected $allowedKeys = array( 'get', 'post', 'files', 'server', 'env', 'cookies', 'urlParams', 'parameters', 'method', 'requesttoken', ); /** @var ISecureRandom */ protected $secureRandom; /** @var IConfig */ protected $config; /** @var string */ protected $requestId = ''; /** @var ICrypto */ protected $crypto; /** @var CsrfTokenManager|null */ protected $csrfTokenManager; /** @var bool */ protected $contentDecoded = false; /** * @param array $vars An associative array with the following optional values: * - array 'urlParams' the parameters which were matched from the URL * - array 'get' the $_GET array * - array|string 'post' the $_POST array or JSON string * - array 'files' the $_FILES array * - array 'server' the $_SERVER array * - array 'env' the $_ENV array * - array 'cookies' the $_COOKIE array * - string 'method' the request method (GET, POST etc) * - string|false 'requesttoken' the requesttoken or false when not available * @param ISecureRandom $secureRandom * @param IConfig $config * @param CsrfTokenManager|null $csrfTokenManager * @param string $stream * @see http://www.php.net/manual/en/reserved.variables.php */ public function __construct(array $vars=array(), ISecureRandom $secureRandom = null, IConfig $config, CsrfTokenManager $csrfTokenManager = null, $stream = 'php://input') { $this->inputStream = $stream; $this->items['params'] = array(); $this->secureRandom = $secureRandom; $this->config = $config; $this->csrfTokenManager = $csrfTokenManager; if(!array_key_exists('method', $vars)) { $vars['method'] = 'GET'; } foreach($this->allowedKeys as $name) { $this->items[$name] = isset($vars[$name]) ? $vars[$name] : array(); } $this->items['parameters'] = array_merge( $this->items['get'], $this->items['post'], $this->items['urlParams'], $this->items['params'] ); } /** * @param array $parameters */ public function setUrlParameters(array $parameters) { $this->items['urlParams'] = $parameters; $this->items['parameters'] = array_merge( $this->items['parameters'], $this->items['urlParams'] ); } /** * Countable method * @return int */ public function count() { return count(array_keys($this->items['parameters'])); } /** * ArrayAccess methods * * Gives access to the combined GET, POST and urlParams arrays * * Examples: * * $var = $request['myvar']; * * or * * if(!isset($request['myvar']) { * // Do something * } * * $request['myvar'] = 'something'; // This throws an exception. * * @param string $offset The key to lookup * @return boolean */ public function offsetExists($offset) { return isset($this->items['parameters'][$offset]); } /** * @see offsetExists */ public function offsetGet($offset) { return isset($this->items['parameters'][$offset]) ? $this->items['parameters'][$offset] : null; } /** * @see offsetExists */ public function offsetSet($offset, $value) { throw new \RuntimeException('You cannot change the contents of the request object'); } /** * @see offsetExists */ public function offsetUnset($offset) { throw new \RuntimeException('You cannot change the contents of the request object'); } /** * Magic property accessors * @param string $name * @param mixed $value */ public function __set($name, $value) { throw new \RuntimeException('You cannot change the contents of the request object'); } /** * Access request variables by method and name. * Examples: * * $request->post['myvar']; // Only look for POST variables * $request->myvar; or $request->{'myvar'}; or $request->{$myvar} * Looks in the combined GET, POST and urlParams array. * * If you access e.g. ->post but the current HTTP request method * is GET a \LogicException will be thrown. * * @param string $name The key to look for. * @throws \LogicException * @return mixed|null */ public function __get($name) { switch($name) { case 'put': case 'patch': case 'get': case 'post': if($this->method !== strtoupper($name)) { throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method)); } return $this->getContent(); case 'files': case 'server': case 'env': case 'cookies': case 'urlParams': case 'method': return isset($this->items[$name]) ? $this->items[$name] : null; case 'parameters': case 'params': return $this->getContent(); default; return isset($this[$name]) ? $this[$name] : null; } } /** * @param string $name * @return bool */ public function __isset($name) { if (in_array($name, $this->allowedKeys, true)) { return true; } return isset($this->items['parameters'][$name]); } /** * @param string $id */ public function __unset($id) { throw new \RuntimeException('You cannot change the contents of the request object'); } /** * Returns the value for a specific http header. * * This method returns null if the header did not exist. * * @param string $name * @return string */ public function getHeader($name) { $name = strtoupper(str_replace(array('-'),array('_'),$name)); if (isset($this->server['HTTP_' . $name])) { return $this->server['HTTP_' . $name]; } // There's a few headers that seem to end up in the top-level // server array. switch($name) { case 'CONTENT_TYPE' : case 'CONTENT_LENGTH' : if (isset($this->server[$name])) { return $this->server[$name]; } break; } return null; } /** * Lets you access post and get parameters by the index * In case of json requests the encoded json body is accessed * * @param string $key the key which you want to access in the URL Parameter * placeholder, $_POST or $_GET array. * The priority how they're returned is the following: * 1. URL parameters * 2. POST parameters * 3. GET parameters * @param mixed $default If the key is not found, this value will be returned * @return mixed the content of the array */ public function getParam($key, $default = null) { return isset($this->parameters[$key]) ? $this->parameters[$key] : $default; } /** * Returns all params that were received, be it from the request * (as GET or POST) or throuh the URL by the route * @return array the array with all parameters */ public function getParams() { return $this->parameters; } /** * Returns the method of the request * @return string the method of the request (POST, GET, etc) */ public function getMethod() { return $this->method; } /** * Shortcut for accessing an uploaded file through the $_FILES array * @param string $key the key that will be taken from the $_FILES array * @return array the file in the $_FILES element */ public function getUploadedFile($key) { return isset($this->files[$key]) ? $this->files[$key] : null; } /** * Shortcut for getting env variables * @param string $key the key that will be taken from the $_ENV array * @return array the value in the $_ENV element */ public function getEnv($key) { return isset($this->env[$key]) ? $this->env[$key] : null; } /** * Shortcut for getting cookie variables * @param string $key the key that will be taken from the $_COOKIE array * @return string the value in the $_COOKIE element */ public function getCookie($key) { return isset($this->cookies[$key]) ? $this->cookies[$key] : null; } /** * Returns the request body content. * * If the HTTP request method is PUT and the body * not application/x-www-form-urlencoded or application/json a stream * resource is returned, otherwise an array. * * @return array|string|resource The request body content or a resource to read the body stream. * * @throws \LogicException */ protected function getContent() { // If the content can't be parsed into an array then return a stream resource. if ($this->method === 'PUT' && $this->getHeader('Content-Length') !== 0 && $this->getHeader('Content-Length') !== null && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false && strpos($this->getHeader('Content-Type'), 'application/json') === false ) { if ($this->content === false) { throw new \LogicException( '"put" can only be accessed once if not ' . 'application/x-www-form-urlencoded or application/json.' ); } $this->content = false; return fopen($this->inputStream, 'rb'); } else { $this->decodeContent(); return $this->items['parameters']; } } /** * Attempt to decode the content and populate parameters */ protected function decodeContent() { if ($this->contentDecoded) { return; } $params = []; // 'application/json' must be decoded manually. if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) { $params = json_decode(file_get_contents($this->inputStream), true); if(count($params) > 0) { $this->items['params'] = $params; if($this->method === 'POST') { $this->items['post'] = $params; } } // Handle application/x-www-form-urlencoded for methods other than GET // or post correctly } elseif($this->method !== 'GET' && $this->method !== 'POST' && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) { parse_str(file_get_contents($this->inputStream), $params); if(is_array($params)) { $this->items['params'] = $params; } } if (is_array($params)) { $this->items['parameters'] = array_merge($this->items['parameters'], $params); } $this->contentDecoded = true; } /** * Checks if the CSRF check was correct * @return bool true if CSRF check passed */ public function passesCSRFCheck() { if($this->csrfTokenManager === null) { return false; } if(!$this->passesStrictCookieCheck()) { return false; } if (isset($this->items['get']['requesttoken'])) { $token = $this->items['get']['requesttoken']; } elseif (isset($this->items['post']['requesttoken'])) { $token = $this->items['post']['requesttoken']; } elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) { $token = $this->items['server']['HTTP_REQUESTTOKEN']; } else { //no token found. return false; } $token = new CsrfToken($token); return $this->csrfTokenManager->isTokenValid($token); } /** * Whether the cookie checks are required * * @return bool */ private function cookieCheckRequired() { if ($this->getHeader('OCS-APIREQUEST')) { return false; } if($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) { return false; } return true; } /** * Wrapper around session_get_cookie_params * * @return array */ protected function getCookieParams() { return session_get_cookie_params(); } /** * Appends the __Host- prefix to the cookie if applicable * * @param string $name * @return string */ protected function getProtectedCookieName($name) { $cookieParams = $this->getCookieParams(); $prefix = ''; if($cookieParams['secure'] === true && $cookieParams['path'] === '/') { $prefix = '__Host-'; } return $prefix.$name; } /** * Checks if the strict cookie has been sent with the request if the request * is including any cookies. * * @return bool * @since 9.1.0 */ public function passesStrictCookieCheck() { if(!$this->cookieCheckRequired()) { return true; } $cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict'); if($this->getCookie($cookieName) === 'true' && $this->passesLaxCookieCheck()) { return true; } return false; } /** * Checks if the lax cookie has been sent with the request if the request * is including any cookies. * * @return bool * @since 9.1.0 */ public function passesLaxCookieCheck() { if(!$this->cookieCheckRequired()) { return true; } $cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax'); if($this->getCookie($cookieName) === 'true') { return true; } return false; } /** * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging * If `mod_unique_id` is installed this value will be taken. * @return string */ public function getId() { if(isset($this->server['UNIQUE_ID'])) { return $this->server['UNIQUE_ID']; } if(empty($this->requestId)) { $validChars = ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS; $this->requestId = $this->secureRandom->generate(20, $validChars); } return $this->requestId; } /** * Returns the remote address, if the connection came from a trusted proxy * and `forwarded_for_headers` has been configured then the IP address * specified in this header will be returned instead. * Do always use this instead of $_SERVER['REMOTE_ADDR'] * @return string IP address */ public function getRemoteAddress() { $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : ''; $trustedProxies = $this->config->getSystemValue('trusted_proxies', []); if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [ 'HTTP_X_FORWARDED_FOR' // only have one default, so we cannot ship an insecure product out of the box ]); foreach($forwardedForHeaders as $header) { if(isset($this->server[$header])) { foreach(explode(',', $this->server[$header]) as $IP) { $IP = trim($IP); if (filter_var($IP, FILTER_VALIDATE_IP) !== false) { return $IP; } } } } } return $remoteAddress; } /** * Check overwrite condition * @param string $type * @return bool */ private function isOverwriteCondition($type = '') { $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '') . '/'; $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : ''; return $regex === '//' || preg_match($regex, $remoteAddr) === 1 || $type !== 'protocol'; } /** * Returns the server protocol. It respects one or more reverse proxies servers * and load balancers * @return string Server protocol (http or https) */ public function getServerProtocol() { if($this->config->getSystemValue('overwriteprotocol') !== '' && $this->isOverwriteCondition('protocol')) { return $this->config->getSystemValue('overwriteprotocol'); } if (isset($this->server['HTTP_X_FORWARDED_PROTO'])) { if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) { $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']); $proto = strtolower(trim($parts[0])); } else { $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']); } // Verify that the protocol is always HTTP or HTTPS // default to http if an invalid value is provided return $proto === 'https' ? 'https' : 'http'; } if (isset($this->server['HTTPS']) && $this->server['HTTPS'] !== null && $this->server['HTTPS'] !== 'off' && $this->server['HTTPS'] !== '') { return 'https'; } return 'http'; } /** * Returns the used HTTP protocol. * * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0. */ public function getHttpProtocol() { $claimedProtocol = strtoupper($this->server['SERVER_PROTOCOL']); $validProtocols = [ 'HTTP/1.0', 'HTTP/1.1', 'HTTP/2', ]; if(in_array($claimedProtocol, $validProtocols, true)) { return $claimedProtocol; } return 'HTTP/1.1'; } /** * Returns the request uri, even if the website uses one or more * reverse proxies * @return string */ public function getRequestUri() { $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; if($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) { $uri = $this->getScriptName() . substr($uri, strlen($this->server['SCRIPT_NAME'])); } return $uri; } /** * Get raw PathInfo from request (not urldecoded) * @throws \Exception * @return string Path info */ public function getRawPathInfo() { $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; // remove too many leading slashes - can be caused by reverse proxy configuration if (strpos($requestUri, '/') === 0) { $requestUri = '/' . ltrim($requestUri, '/'); } $requestUri = preg_replace('%/{2,}%', '/', $requestUri); // Remove the query string from REQUEST_URI if ($pos = strpos($requestUri, '?')) { $requestUri = substr($requestUri, 0, $pos); } $scriptName = $this->server['SCRIPT_NAME']; $pathInfo = $requestUri; // strip off the script name's dir and file name // FIXME: Sabre does not really belong here list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($scriptName); if (!empty($path)) { if($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) { $pathInfo = substr($pathInfo, strlen($path)); } else { throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')"); } } if (strpos($pathInfo, '/'.$name) === 0) { $pathInfo = substr($pathInfo, strlen($name) + 1); } if (strpos($pathInfo, $name) === 0) { $pathInfo = substr($pathInfo, strlen($name)); } if($pathInfo === false || $pathInfo === '/'){ return ''; } else { return $pathInfo; } } /** * Get PathInfo from request * @throws \Exception * @return string|false Path info or false when not found */ public function getPathInfo() { $pathInfo = $this->getRawPathInfo(); // following is taken from \Sabre\HTTP\URLUtil::decodePathSegment $pathInfo = rawurldecode($pathInfo); $encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']); switch($encoding) { case 'ISO-8859-1' : $pathInfo = utf8_encode($pathInfo); } // end copy return $pathInfo; } /** * Returns the script name, even if the website uses one or more * reverse proxies * @return string the script name */ public function getScriptName() { $name = $this->server['SCRIPT_NAME']; $overwriteWebRoot = $this->config->getSystemValue('overwritewebroot'); if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) { // FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -strlen('lib/private/appframework/http/'))); $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), strlen($serverRoot))); $name = '/' . ltrim($overwriteWebRoot . $suburi, '/'); } return $name; } /** * Checks whether the user agent matches a given regex * @param array $agent array of agent names * @return bool true if at least one of the given agent matches, false otherwise */ public function isUserAgent(array $agent) { if (!isset($this->server['HTTP_USER_AGENT'])) { return false; } foreach ($agent as $regex) { if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) { return true; } } return false; } /** * Returns the unverified server host from the headers without checking * whether it is a trusted domain * @return string Server host */ public function getInsecureServerHost() { $host = 'localhost'; if (isset($this->server['HTTP_X_FORWARDED_HOST'])) { if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) { $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']); $host = trim(current($parts)); } else { $host = $this->server['HTTP_X_FORWARDED_HOST']; } } else { if (isset($this->server['HTTP_HOST'])) { $host = $this->server['HTTP_HOST']; } else if (isset($this->server['SERVER_NAME'])) { $host = $this->server['SERVER_NAME']; } } return $host; } /** * Returns the server host from the headers, or the first configured * trusted domain if the host isn't in the trusted list * @return string Server host */ public function getServerHost() { // overwritehost is always trusted $host = $this->getOverwriteHost(); if ($host !== null) { return $host; } // get the host from the headers $host = $this->getInsecureServerHost(); // Verify that the host is a trusted domain if the trusted domains // are defined // If no trusted domain is provided the first trusted domain is returned $trustedDomainHelper = new TrustedDomainHelper($this->config); if ($trustedDomainHelper->isTrustedDomain($host)) { return $host; } else { $trustedList = $this->config->getSystemValue('trusted_domains', []); if(!empty($trustedList)) { return $trustedList[0]; } else { return ''; } } } /** * Returns the overwritehost setting from the config if set and * if the overwrite condition is met * @return string|null overwritehost value or null if not defined or the defined condition * isn't met */ private function getOverwriteHost() { if($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) { return $this->config->getSystemValue('overwritehost'); } return null; } } private/AppFramework/Http/Output.php 0000604 00000004412 15247130452 0013544 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Http; use OCP\AppFramework\Http\IOutput; /** * Very thin wrapper class to make output testable */ class Output implements IOutput { /** @var string */ private $webRoot; /** * @param $webRoot */ public function __construct($webRoot) { $this->webRoot = $webRoot; } /** * @param string $out */ public function setOutput($out) { print($out); } /** * @param string|resource $path or file handle * * @return bool false if an error occurred */ public function setReadfile($path) { if (is_resource($path)) { $output = fopen('php://output', 'w'); return stream_copy_to_stream($path, $output) > 0; } else { return @readfile($path); } } /** * @param string $header */ public function setHeader($header) { header($header); } /** * @param int $code sets the http status code */ public function setHttpResponseCode($code) { http_response_code($code); } /** * @return int returns the current http response code */ public function getHttpResponseCode() { return http_response_code(); } /** * @param string $name * @param string $value * @param int $expire * @param string $path * @param string $domain * @param bool $secure * @param bool $httpOnly */ public function setCookie($name, $value, $expire, $path, $domain, $secure, $httpOnly) { $path = $this->webRoot ? : '/'; setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); } } private/AppFramework/Http/Dispatcher.php 0000604 00000013413 15247130452 0014333 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Georg Ehrke <georg@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Http; use \OC\AppFramework\Middleware\MiddlewareDispatcher; use \OC\AppFramework\Http; use \OC\AppFramework\Utility\ControllerMethodReflector; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; /** * Class to dispatch the request to the middleware dispatcher */ class Dispatcher { private $middlewareDispatcher; private $protocol; private $reflector; private $request; /** * @param Http $protocol the http protocol with contains all status headers * @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which * runs the middleware * @param ControllerMethodReflector $reflector the reflector that is used to inject * the arguments for the controller * @param IRequest $request the incoming request */ public function __construct(Http $protocol, MiddlewareDispatcher $middlewareDispatcher, ControllerMethodReflector $reflector, IRequest $request) { $this->protocol = $protocol; $this->middlewareDispatcher = $middlewareDispatcher; $this->reflector = $reflector; $this->request = $request; } /** * Handles a request and calls the dispatcher on the controller * @param Controller $controller the controller which will be called * @param string $methodName the method name which will be called on * the controller * @return array $array[0] contains a string with the http main header, * $array[1] contains headers in the form: $key => value, $array[2] contains * the response output * @throws \Exception */ public function dispatch(Controller $controller, $methodName) { $out = array(null, array(), null); try { // prefill reflector with everything thats needed for the // middlewares $this->reflector->reflect($controller, $methodName); $this->middlewareDispatcher->beforeController($controller, $methodName); $response = $this->executeController($controller, $methodName); // if an exception appears, the middleware checks if it can handle the // exception and creates a response. If no response is created, it is // assumed that theres no middleware who can handle it and the error is // thrown again } catch(\Exception $exception){ $response = $this->middlewareDispatcher->afterException( $controller, $methodName, $exception); if (is_null($response)) { throw $exception; } } $response = $this->middlewareDispatcher->afterController( $controller, $methodName, $response); // depending on the cache object the headers need to be changed $out[0] = $this->protocol->getStatusHeader($response->getStatus(), $response->getLastModified(), $response->getETag()); $out[1] = array_merge($response->getHeaders()); $out[2] = $response->getCookies(); $out[3] = $this->middlewareDispatcher->beforeOutput( $controller, $methodName, $response->render() ); $out[4] = $response; return $out; } /** * Uses the reflected parameters, types and request parameters to execute * the controller * @param Controller $controller the controller to be executed * @param string $methodName the method on the controller that should be executed * @return Response */ private function executeController($controller, $methodName) { $arguments = array(); // valid types that will be casted $types = array('int', 'integer', 'bool', 'boolean', 'float'); foreach($this->reflector->getParameters() as $param => $default) { // try to get the parameter from the request object and cast // it to the type annotated in the @param annotation $value = $this->request->getParam($param, $default); $type = $this->reflector->getType($param); // if this is submitted using GET or a POST form, 'false' should be // converted to false if(($type === 'bool' || $type === 'boolean') && $value === 'false' && ( $this->request->method === 'GET' || strpos($this->request->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false ) ) { $value = false; } elseif($value !== null && in_array($type, $types)) { settype($value, $type); } $arguments[] = $value; } $response = call_user_func_array(array($controller, $methodName), $arguments); // format response if($response instanceof DataResponse || !($response instanceof Response)) { // get format from the url format or request format parameter $format = $this->request->getParam('format'); // if none is given try the first Accept header if($format === null) { $headers = $this->request->getHeader('Accept'); $format = $controller->getResponderByHTTPHeader($headers, null); } if ($format !== null) { $response = $controller->buildResponse($response, $format); } else { $response = $controller->buildResponse($response); } } return $response; } } private/AppFramework/Utility/SimpleContainer.php 0000604 00000012042 15247130452 0016062 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Utility; use ReflectionClass; use ReflectionException; use Closure; use Pimple\Container; use OCP\AppFramework\QueryException; use OCP\IContainer; /** * Class SimpleContainer * * SimpleContainer is a simple implementation of IContainer on basis of Pimple */ class SimpleContainer extends Container implements IContainer { /** * @param ReflectionClass $class the class to instantiate * @return \stdClass the created class */ private function buildClass(ReflectionClass $class) { $constructor = $class->getConstructor(); if ($constructor === null) { return $class->newInstance(); } else { $parameters = []; foreach ($constructor->getParameters() as $parameter) { $parameterClass = $parameter->getClass(); // try to find out if it is a class or a simple parameter if ($parameterClass === null) { $resolveName = $parameter->getName(); } else { $resolveName = $parameterClass->name; } try { $parameters[] = $this->query($resolveName); } catch (\Exception $e) { // Service not found, use the default value when available if ($parameter->isDefaultValueAvailable()) { $parameters[] = $parameter->getDefaultValue(); } else if ($parameterClass !== null) { $resolveName = $parameter->getName(); $parameters[] = $this->query($resolveName); } else { throw $e; } } } return $class->newInstanceArgs($parameters); } } /** * If a parameter is not registered in the container try to instantiate it * by using reflection to find out how to build the class * @param string $name the class name to resolve * @return \stdClass * @throws QueryException if the class could not be found or instantiated */ public function resolve($name) { $baseMsg = 'Could not resolve ' . $name . '!'; try { $class = new ReflectionClass($name); if ($class->isInstantiable()) { return $this->buildClass($class); } else { throw new QueryException($baseMsg . ' Class can not be instantiated'); } } catch(ReflectionException $e) { throw new QueryException($baseMsg . ' ' . $e->getMessage()); } } /** * @param string $name name of the service to query for * @return mixed registered service for the given $name * @throws QueryException if the query could not be resolved */ public function query($name) { $name = $this->sanitizeName($name); if ($this->offsetExists($name)) { return $this->offsetGet($name); } else { $object = $this->resolve($name); $this->registerService($name, function () use ($object) { return $object; }); return $object; } } /** * @param string $name * @param mixed $value */ public function registerParameter($name, $value) { $this[$name] = $value; } /** * The given closure is call the first time the given service is queried. * The closure has to return the instance for the given service. * Created instance will be cached in case $shared is true. * * @param string $name name of the service to register another backend for * @param Closure $closure the closure to be called on service creation * @param bool $shared */ public function registerService($name, Closure $closure, $shared = true) { $name = $this->sanitizeName($name); if (isset($this[$name])) { unset($this[$name]); } if ($shared) { $this[$name] = $closure; } else { $this[$name] = parent::factory($closure); } } /** * Shortcut for returning a service from a service under a different key, * e.g. to tell the container to return a class when queried for an * interface * @param string $alias the alias that should be registered * @param string $target the target that should be resolved instead */ public function registerAlias($alias, $target) { $this->registerService($alias, function (IContainer $container) use ($target) { return $container->query($target); }, false); } /* * @param string $name * @return string */ protected function sanitizeName($name) { if (isset($name[0]) && $name[0] === '\\') { return ltrim($name, '\\'); } return $name; } } private/AppFramework/Utility/TimeFactory.php 0000604 00000002163 15247130452 0015217 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Utility; use OCP\AppFramework\Utility\ITimeFactory; /** * Needed to mock calls to time() */ class TimeFactory implements ITimeFactory { /** * @return int the result of a call to time() */ public function getTime() { return time(); } } private/AppFramework/Utility/ControllerMethodReflector.php 0000604 00000010105 15247130452 0020116 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Utility; use \OCP\AppFramework\Utility\IControllerMethodReflector; /** * Reads and parses annotations from doc comments */ class ControllerMethodReflector implements IControllerMethodReflector { public $annotations = []; private $types = []; private $parameters = []; /** * @param object $object an object or classname * @param string $method the method which we want to inspect */ public function reflect($object, $method){ $reflection = new \ReflectionMethod($object, $method); $docs = $reflection->getDocComment(); // extract everything prefixed by @ and first letter uppercase preg_match_all('/^\h+\*\h+@(?P<annotation>[A-Z]\w+)((?P<parameter>.*))?$/m', $docs, $matches); foreach($matches['annotation'] as $key => $annontation) { $annotationValue = $matches['parameter'][$key]; if(isset($annotationValue[0]) && $annotationValue[0] === '(' && $annotationValue[strlen($annotationValue) - 1] === ')') { $cutString = substr($annotationValue, 1, -1); $cutString = str_replace(' ', '', $cutString); $splittedArray = explode(',', $cutString); foreach($splittedArray as $annotationValues) { list($key, $value) = explode('=', $annotationValues); $this->annotations[$annontation][$key] = $value; } continue; } $this->annotations[$annontation] = [$annotationValue]; } // extract type parameter information preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches); $this->types = array_combine($matches['var'], $matches['type']); foreach ($reflection->getParameters() as $param) { // extract type information from PHP 7 scalar types and prefer them // over phpdoc annotations if (method_exists($param, 'getType')) { $type = $param->getType(); if ($type !== null) { $this->types[$param->getName()] = (string) $type; } } if($param->isOptional()) { $default = $param->getDefaultValue(); } else { $default = null; } $this->parameters[$param->name] = $default; } } /** * Inspects the PHPDoc parameters for types * @param string $parameter the parameter whose type comments should be * parsed * @return string|null type in the type parameters (@param int $something) * would return int or null if not existing */ public function getType($parameter) { if(array_key_exists($parameter, $this->types)) { return $this->types[$parameter]; } else { return null; } } /** * @return array the arguments of the method with key => default value */ public function getParameters() { return $this->parameters; } /** * Check if a method contains an annotation * @param string $name the name of the annotation * @return bool true if the annotation is found */ public function hasAnnotation($name) { return array_key_exists($name, $this->annotations); } /** * Get optional annotation parameter by key * * @param string $name the name of the annotation * @param string $key the string of the annotation * @return string */ public function getAnnotationParameter($name, $key) { if(isset($this->annotations[$name][$key])) { return $this->annotations[$name][$key]; } return ''; } } private/AppFramework/App.php 0000604 00000013745 15247130452 0012056 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\DependencyInjection\DIContainer; use OCP\AppFramework\Http; use OCP\AppFramework\QueryException; use OCP\AppFramework\Http\ICallbackResponse; /** * Entry point for every request in your app. You can consider this as your * public static void main() method * * Handles all the dependency injection, controllers and output flow */ class App { /** @var string[] */ private static $nameSpaceCache = []; /** * Turns an app id into a namespace by either reading the appinfo.xml's * namespace tag or uppercasing the appid's first letter * @param string $appId the app id * @param string $topNamespace the namespace which should be prepended to * the transformed app id, defaults to OCA\ * @return string the starting namespace for the app */ public static function buildAppNamespace($appId, $topNamespace='OCA\\') { // Hit the cache! if (isset(self::$nameSpaceCache[$appId])) { return $topNamespace . self::$nameSpaceCache[$appId]; } $appInfo = \OC_App::getAppInfo($appId); if (isset($appInfo['namespace'])) { self::$nameSpaceCache[$appId] = trim($appInfo['namespace']); } else { // if the tag is not found, fall back to uppercasing the first letter self::$nameSpaceCache[$appId] = ucfirst($appId); } return $topNamespace . self::$nameSpaceCache[$appId]; } /** * Shortcut for calling a controller method and printing the result * @param string $controllerName the name of the controller under which it is * stored in the DI container * @param string $methodName the method that you want to call * @param DIContainer $container an instance of a pimple container. * @param array $urlParams list of URL parameters (optional) */ public static function main($controllerName, $methodName, DIContainer $container, array $urlParams = null) { if (!is_null($urlParams)) { $container['OCP\\IRequest']->setUrlParameters($urlParams); } else if (isset($container['urlParams']) && !is_null($container['urlParams'])) { $container['OCP\\IRequest']->setUrlParameters($container['urlParams']); } $appName = $container['AppName']; // first try $controllerName then go for \OCA\AppName\Controller\$controllerName try { $controller = $container->query($controllerName); } catch(QueryException $e) { if ($appName === 'core') { $appNameSpace = 'OC\\Core'; } else if ($appName === 'settings') { $appNameSpace = 'OC\\Settings'; } else { $appNameSpace = self::buildAppNamespace($appName); } $controllerName = $appNameSpace . '\\Controller\\' . $controllerName; $controller = $container->query($controllerName); } // initialize the dispatcher and run all the middleware before the controller /** @var Dispatcher $dispatcher */ $dispatcher = $container['Dispatcher']; list( $httpHeaders, $responseHeaders, $responseCookies, $output, $response ) = $dispatcher->dispatch($controller, $methodName); $io = $container['OCP\\AppFramework\\Http\\IOutput']; if(!is_null($httpHeaders)) { $io->setHeader($httpHeaders); } foreach($responseHeaders as $name => $value) { $io->setHeader($name . ': ' . $value); } foreach($responseCookies as $name => $value) { $expireDate = null; if($value['expireDate'] instanceof \DateTime) { $expireDate = $value['expireDate']->getTimestamp(); } $io->setCookie( $name, $value['value'], $expireDate, $container->getServer()->getWebRoot(), null, $container->getServer()->getRequest()->getServerProtocol() === 'https', true ); } /* * Status 204 does not have a body and no Content Length * Status 304 does not have a body and does not need a Content Length * https://tools.ietf.org/html/rfc7230#section-3.3 * https://tools.ietf.org/html/rfc7230#section-3.3.2 */ if ($httpHeaders !== Http::STATUS_NO_CONTENT && $httpHeaders !== Http::STATUS_NOT_MODIFIED) { if ($response instanceof ICallbackResponse) { $response->callback($io); } else if (!is_null($output)) { $io->setHeader('Content-Length: ' . strlen($output)); $io->setOutput($output); } } } /** * Shortcut for calling a controller method and printing the result. * Similar to App:main except that no headers will be sent. * This should be used for example when registering sections via * \OC\AppFramework\Core\API::registerAdmin() * * @param string $controllerName the name of the controller under which it is * stored in the DI container * @param string $methodName the method that you want to call * @param array $urlParams an array with variables extracted from the routes * @param DIContainer $container an instance of a pimple container. */ public static function part($controllerName, $methodName, array $urlParams, DIContainer $container){ $container['urlParams'] = $urlParams; $controller = $container[$controllerName]; $dispatcher = $container['Dispatcher']; list(, , $output) = $dispatcher->dispatch($controller, $methodName); return $output; } } private/AppFramework/Routing/RouteConfig.php 0000604 00000017642 15247130452 0015211 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Patrick Paysant <ppaysant@linagora.com> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Routing; use OC\AppFramework\DependencyInjection\DIContainer; use OCP\Route\IRouter; /** * Class RouteConfig * @package OC\AppFramework\routing */ class RouteConfig { /** @var DIContainer */ private $container; /** @var IRouter */ private $router; /** @var array */ private $routes; /** @var string */ private $appName; /** @var string[] */ private $controllerNameCache = []; /** * @param \OC\AppFramework\DependencyInjection\DIContainer $container * @param \OCP\Route\IRouter $router * @param array $routes * @internal param $appName */ public function __construct(DIContainer $container, IRouter $router, $routes) { $this->routes = $routes; $this->container = $container; $this->router = $router; $this->appName = $container['AppName']; } /** * The routes and resource will be registered to the \OCP\Route\IRouter */ public function register() { // parse simple $this->processSimpleRoutes($this->routes); // parse resources $this->processResources($this->routes); /* * OCS routes go into a different collection */ $oldCollection = $this->router->getCurrentCollection(); $this->router->useCollection($oldCollection.'.ocs'); // parse ocs simple routes $this->processOCS($this->routes); $this->router->useCollection($oldCollection); } private function processOCS(array $routes) { $ocsRoutes = isset($routes['ocs']) ? $routes['ocs'] : []; foreach ($ocsRoutes as $ocsRoute) { $name = $ocsRoute['name']; $postfix = ''; if (isset($ocsRoute['postfix'])) { $postfix = $ocsRoute['postfix']; } if (isset($ocsRoute['root'])) { $root = $ocsRoute['root']; } else { $root = '/apps/'.$this->appName; } $url = $root . $ocsRoute['url']; $verb = isset($ocsRoute['verb']) ? strtoupper($ocsRoute['verb']) : 'GET'; $split = explode('#', $name, 2); if (count($split) != 2) { throw new \UnexpectedValueException('Invalid route name'); } $controller = $split[0]; $action = $split[1]; $controllerName = $this->buildControllerName($controller); $actionName = $this->buildActionName($action); // register the route $handler = new RouteActionHandler($this->container, $controllerName, $actionName); $router = $this->router->create('ocs.'.$this->appName.'.'.$controller.'.'.$action . $postfix, $url) ->method($verb) ->action($handler); // optionally register requirements for route. This is used to // tell the route parser how url parameters should be matched if(array_key_exists('requirements', $ocsRoute)) { $router->requirements($ocsRoute['requirements']); } // optionally register defaults for route. This is used to // tell the route parser how url parameters should be default valued if(array_key_exists('defaults', $ocsRoute)) { $router->defaults($ocsRoute['defaults']); } } } /** * Creates one route base on the give configuration * @param array $routes * @throws \UnexpectedValueException */ private function processSimpleRoutes($routes) { $simpleRoutes = isset($routes['routes']) ? $routes['routes'] : array(); foreach ($simpleRoutes as $simpleRoute) { $name = $simpleRoute['name']; $postfix = ''; if (isset($simpleRoute['postfix'])) { $postfix = $simpleRoute['postfix']; } $url = $simpleRoute['url']; $verb = isset($simpleRoute['verb']) ? strtoupper($simpleRoute['verb']) : 'GET'; $split = explode('#', $name, 2); if (count($split) != 2) { throw new \UnexpectedValueException('Invalid route name'); } $controller = $split[0]; $action = $split[1]; $controllerName = $this->buildControllerName($controller); $actionName = $this->buildActionName($action); // register the route $handler = new RouteActionHandler($this->container, $controllerName, $actionName); $router = $this->router->create($this->appName.'.'.$controller.'.'.$action . $postfix, $url) ->method($verb) ->action($handler); // optionally register requirements for route. This is used to // tell the route parser how url parameters should be matched if(array_key_exists('requirements', $simpleRoute)) { $router->requirements($simpleRoute['requirements']); } // optionally register defaults for route. This is used to // tell the route parser how url parameters should be default valued if(array_key_exists('defaults', $simpleRoute)) { $router->defaults($simpleRoute['defaults']); } } } /** * For a given name and url restful routes are created: * - index * - show * - new * - create * - update * - destroy * * @param array $routes */ private function processResources($routes) { // declaration of all restful actions $actions = array( array('name' => 'index', 'verb' => 'GET', 'on-collection' => true), array('name' => 'show', 'verb' => 'GET'), array('name' => 'create', 'verb' => 'POST', 'on-collection' => true), array('name' => 'update', 'verb' => 'PUT'), array('name' => 'destroy', 'verb' => 'DELETE'), ); $resources = isset($routes['resources']) ? $routes['resources'] : array(); foreach ($resources as $resource => $config) { // the url parameter used as id to the resource foreach($actions as $action) { $url = $config['url']; $method = $action['name']; $verb = isset($action['verb']) ? strtoupper($action['verb']) : 'GET'; $collectionAction = isset($action['on-collection']) ? $action['on-collection'] : false; if (!$collectionAction) { $url = $url . '/{id}'; } if (isset($action['url-postfix'])) { $url = $url . '/' . $action['url-postfix']; } $controller = $resource; $controllerName = $this->buildControllerName($controller); $actionName = $this->buildActionName($method); $routeName = $this->appName . '.' . strtolower($resource) . '.' . strtolower($method); $this->router->create($routeName, $url)->method($verb)->action( new RouteActionHandler($this->container, $controllerName, $actionName) ); } } } /** * Based on a given route name the controller name is generated * @param string $controller * @return string */ private function buildControllerName($controller) { if (!isset($this->controllerNameCache[$controller])) { $this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller'; } return $this->controllerNameCache[$controller]; } /** * Based on the action part of the route name the controller method name is generated * @param string $action * @return string */ private function buildActionName($action) { return $this->underScoreToCamelCase($action); } /** * Underscored strings are converted to camel case strings * @param string $str * @return string */ private function underScoreToCamelCase($str) { $pattern = "/_[a-z]?/"; return preg_replace_callback( $pattern, function ($matches) { return strtoupper(ltrim($matches[0], "_")); }, $str); } } private/AppFramework/Routing/RouteActionHandler.php 0000604 00000002757 15247130452 0016520 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Routing; use \OC\AppFramework\App; use \OC\AppFramework\DependencyInjection\DIContainer; class RouteActionHandler { private $controllerName; private $actionName; private $container; /** * @param string $controllerName * @param string $actionName */ public function __construct(DIContainer $container, $controllerName, $actionName) { $this->controllerName = $controllerName; $this->actionName = $actionName; $this->container = $container; } public function __invoke($params) { App::main($this->controllerName, $this->actionName, $this->container, $params); } } private/AppFramework/OCS/V1Response.php 0000604 00000004061 15247130452 0013756 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\AppFramework\OCS; use OCP\API; use OCP\AppFramework\Http; class V1Response extends BaseResponse { /** * The V1 endpoint has very limited http status codes basically everything * is status 200 except 401 * * @return int */ public function getStatus() { $status = parent::getStatus(); if ($status === Http::STATUS_FORBIDDEN || $status === API::RESPOND_UNAUTHORISED) { return Http::STATUS_UNAUTHORIZED; } return Http::STATUS_OK; } /** * In v1 all OK is 100 * * @return int */ public function getOCSStatus() { $status = parent::getOCSStatus(); if ($status === Http::STATUS_OK) { return 100; } return $status; } /** * Construct the meta part of the response * And then late the base class render * * @return string */ public function render() { $meta = [ 'status' => $this->getOCSStatus() === 100 ? 'ok' : 'failure', 'statuscode' => $this->getOCSStatus(), 'message' => $this->getOCSStatus() === 100 ? 'OK' : $this->statusMessage, ]; $meta['totalitems'] = $this->itemsCount !== null ? (string)$this->itemsCount : ''; $meta['itemsperpage'] = $this->itemsPerPage !== null ? (string)$this->itemsPerPage: ''; return $this->renderResult($meta); } } private/AppFramework/OCS/BaseResponse.php 0000604 00000005135 15247130452 0014345 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\AppFramework\OCS; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\EmptyContentSecurityPolicy; use OCP\AppFramework\Http\Response; abstract class BaseResponse extends Response { /** @var array */ protected $data; /** @var string */ protected $format; /** @var string */ protected $statusMessage; /** @var int */ protected $itemsCount; /** @var int */ protected $itemsPerPage; /** * BaseResponse constructor. * * @param DataResponse|null $dataResponse * @param string $format * @param string|null $statusMessage * @param int|null $itemsCount * @param int|null $itemsPerPage */ public function __construct(DataResponse $dataResponse, $format = 'xml', $statusMessage = null, $itemsCount = null, $itemsPerPage = null) { $this->format = $format; $this->statusMessage = $statusMessage; $this->itemsCount = $itemsCount; $this->itemsPerPage = $itemsPerPage; $this->data = $dataResponse->getData(); $this->setHeaders($dataResponse->getHeaders()); $this->setStatus($dataResponse->getStatus()); $this->setETag($dataResponse->getETag()); $this->setLastModified($dataResponse->getLastModified()); $this->setCookies($dataResponse->getCookies()); $this->setContentSecurityPolicy(new EmptyContentSecurityPolicy()); if ($format === 'json') { $this->addHeader( 'Content-Type', 'application/json; charset=utf-8' ); } else { $this->addHeader( 'Content-Type', 'application/xml; charset=utf-8' ); } } /** * @param string[] $meta * @return string */ protected function renderResult($meta) { // TODO rewrite functions return \OC_API::renderResult($this->format, $meta, $this->data); } public function getOCSStatus() { return parent::getStatus(); } } private/AppFramework/OCS/V2Response.php 0000604 00000004253 15247130452 0013762 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\AppFramework\OCS; use OCP\AppFramework\Http; use OCP\API; class V2Response extends BaseResponse { /** * The V2 endpoint just passes on status codes. * Of course we have to map the OCS specific codes to proper HTTP status codes * * @return int */ public function getStatus() { $status = parent::getStatus(); if ($status === API::RESPOND_UNAUTHORISED) { return Http::STATUS_UNAUTHORIZED; } else if ($status === API::RESPOND_NOT_FOUND) { return Http::STATUS_NOT_FOUND; } else if ($status === API::RESPOND_SERVER_ERROR || $status === API::RESPOND_UNKNOWN_ERROR) { return Http::STATUS_INTERNAL_SERVER_ERROR; } else if ($status < 200 || $status > 600) { return Http::STATUS_BAD_REQUEST; } return $status; } /** * Construct the meta part of the response * And then late the base class render * * @return string */ public function render() { $status = parent::getStatus(); $meta = [ 'status' => $status >= 200 && $status < 300 ? 'ok' : 'failure', 'statuscode' => $this->getOCSStatus(), 'message' => $status >= 200 && $status < 300 ? 'OK' : $this->statusMessage, ]; if ($this->itemsCount !== null) { $meta['totalitems'] = $this->itemsCount; } if ($this->itemsPerPage !== null) { $meta['itemsperpage'] = $this->itemsPerPage; } return $this->renderResult($meta); } } private/AppFramework/Middleware/OCSMiddleware.php 0000604 00000010366 15247130452 0016031 0 ustar 00 <?php /** * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\AppFramework\Middleware; use OC\AppFramework\Http; use OC\AppFramework\OCS\BaseResponse; use OC\AppFramework\OCS\V1Response; use OC\AppFramework\OCS\V2Response; use OCP\API; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\OCS\OCSException; use OCP\AppFramework\OCSController; use OCP\IRequest; use OCP\AppFramework\Middleware; class OCSMiddleware extends Middleware { /** @var IRequest */ private $request; /** @var int */ private $ocsVersion; /** * @param IRequest $request */ public function __construct(IRequest $request) { $this->request = $request; } /** * @param \OCP\AppFramework\Controller $controller * @param string $methodName */ public function beforeController($controller, $methodName) { if ($controller instanceof OCSController) { if (substr_compare($this->request->getScriptName(), '/ocs/v2.php', -strlen('/ocs/v2.php')) === 0) { $this->ocsVersion = 2; } else { $this->ocsVersion = 1; } $controller->setOCSVersion($this->ocsVersion); } } /** * @param \OCP\AppFramework\Controller $controller * @param string $methodName * @param \Exception $exception * @throws \Exception * @return BaseResponse */ public function afterException($controller, $methodName, \Exception $exception) { if ($controller instanceof OCSController && $exception instanceof OCSException) { $code = $exception->getCode(); if ($code === 0) { $code = API::RESPOND_UNKNOWN_ERROR; } return $this->buildNewResponse($controller, $code, $exception->getMessage()); } throw $exception; } /** * @param \OCP\AppFramework\Controller $controller * @param string $methodName * @param Response $response * @return \OCP\AppFramework\Http\Response */ public function afterController($controller, $methodName, Response $response) { /* * If a different middleware has detected that a request unauthorized or forbidden * we need to catch the response and convert it to a proper OCS response. */ if ($controller instanceof OCSController && !($response instanceof BaseResponse)) { if ($response->getStatus() === Http::STATUS_UNAUTHORIZED || $response->getStatus() === Http::STATUS_FORBIDDEN) { $message = ''; if ($response instanceof JSONResponse) { /** @var DataResponse $response */ $message = $response->getData()['message']; } return $this->buildNewResponse($controller, API::RESPOND_UNAUTHORISED, $message); } } return $response; } /** * @param Controller $controller * @param int $code * @param string $message * @return V1Response|V2Response */ private function buildNewResponse($controller, $code, $message) { $format = $this->getFormat($controller); $data = new DataResponse(); $data->setStatus($code); if ($this->ocsVersion === 1) { $response = new V1Response($data, $format, $message); } else { $response = new V2Response($data, $format, $message); } return $response; } /** * @param \OCP\AppFramework\Controller $controller * @return string */ private function getFormat($controller) { // get format from the url format or request format parameter $format = $this->request->getParam('format'); // if none is given try the first Accept header if($format === null) { $headers = $this->request->getHeader('Accept'); $format = $controller->getResponderByHTTPHeader($headers, 'xml'); } return $format; } } private/AppFramework/Middleware/SessionMiddleware.php 0000604 00000004077 15247130452 0017032 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware; use OC\AppFramework\Utility\ControllerMethodReflector; use OCP\IRequest; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; use OCP\ISession; class SessionMiddleware extends Middleware { /** * @var IRequest */ private $request; /** * @var ControllerMethodReflector */ private $reflector; /** * @param IRequest $request * @param ControllerMethodReflector $reflector */ public function __construct(IRequest $request, ControllerMethodReflector $reflector, ISession $session ) { $this->request = $request; $this->reflector = $reflector; $this->session = $session; } /** * @param \OCP\AppFramework\Controller $controller * @param string $methodName */ public function beforeController($controller, $methodName) { $useSession = $this->reflector->hasAnnotation('UseSession'); if (!$useSession) { $this->session->close(); } } /** * @param \OCP\AppFramework\Controller $controller * @param string $methodName * @param Response $response * @return Response */ public function afterController($controller, $methodName, Response $response){ $useSession = $this->reflector->hasAnnotation('UseSession'); if ($useSession) { $this->session->close(); } return $response; } } private/AppFramework/Middleware/MiddlewareDispatcher.php 0000604 00000012356 15247130452 0017474 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Response; use OCP\AppFramework\MiddleWare; /** * This class is used to store and run all the middleware in correct order */ class MiddlewareDispatcher { /** * @var array array containing all the middlewares */ private $middlewares; /** * @var int counter which tells us what middlware was executed once an * exception occurs */ private $middlewareCounter; /** * Constructor */ public function __construct(){ $this->middlewares = array(); $this->middlewareCounter = 0; } /** * Adds a new middleware * @param Middleware $middleWare the middleware which will be added */ public function registerMiddleware(Middleware $middleWare){ array_push($this->middlewares, $middleWare); } /** * returns an array with all middleware elements * @return array the middlewares */ public function getMiddlewares(){ return $this->middlewares; } /** * This is being run in normal order before the controller is being * called which allows several modifications and checks * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller */ public function beforeController(Controller $controller, $methodName){ // we need to count so that we know which middlewares we have to ask in // case there is an exception $middlewareCount = count($this->middlewares); for($i = 0; $i < $middlewareCount; $i++){ $this->middlewareCounter++; $middleware = $this->middlewares[$i]; $middleware->beforeController($controller, $methodName); } } /** * This is being run when either the beforeController method or the * controller method itself is throwing an exception. The middleware is asked * in reverse order to handle the exception and to return a response. * If the response is null, it is assumed that the exception could not be * handled and the error will be thrown again * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param \Exception $exception the thrown exception * @return Response a Response object if the middleware can handle the * exception * @throws \Exception the passed in exception if it can't handle it */ public function afterException(Controller $controller, $methodName, \Exception $exception){ for($i=$this->middlewareCounter-1; $i>=0; $i--){ $middleware = $this->middlewares[$i]; try { return $middleware->afterException($controller, $methodName, $exception); } catch(\Exception $exception){ continue; } } throw $exception; } /** * This is being run after a successful controllermethod call and allows * the manipulation of a Response object. The middleware is run in reverse order * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param Response $response the generated response from the controller * @return Response a Response object */ public function afterController(Controller $controller, $methodName, Response $response){ for($i=count($this->middlewares)-1; $i>=0; $i--){ $middleware = $this->middlewares[$i]; $response = $middleware->afterController($controller, $methodName, $response); } return $response; } /** * This is being run after the response object has been rendered and * allows the manipulation of the output. The middleware is run in reverse order * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param string $output the generated output from a response * @return string the output that should be printed */ public function beforeOutput(Controller $controller, $methodName, $output){ for($i=count($this->middlewares)-1; $i>=0; $i--){ $middleware = $this->middlewares[$i]; $output = $middleware->beforeOutput($controller, $methodName, $output); } return $output; } } private/AppFramework/Middleware/Security/BruteForceMiddleware.php 0000604 00000005367 15247130452 0021261 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Utility\ControllerMethodReflector; use OC\Security\Bruteforce\Throttler; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; use OCP\IRequest; /** * Class BruteForceMiddleware performs the bruteforce protection for controllers * that are annotated with @BruteForceProtection(action=$action) whereas $action * is the action that should be logged within the database. * * @package OC\AppFramework\Middleware\Security */ class BruteForceMiddleware extends Middleware { /** @var ControllerMethodReflector */ private $reflector; /** @var Throttler */ private $throttler; /** @var IRequest */ private $request; /** * @param ControllerMethodReflector $controllerMethodReflector * @param Throttler $throttler * @param IRequest $request */ public function __construct(ControllerMethodReflector $controllerMethodReflector, Throttler $throttler, IRequest $request) { $this->reflector = $controllerMethodReflector; $this->throttler = $throttler; $this->request = $request; } /** * {@inheritDoc} */ public function beforeController($controller, $methodName) { parent::beforeController($controller, $methodName); if($this->reflector->hasAnnotation('BruteForceProtection')) { $action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action'); $this->throttler->sleepDelay($this->request->getRemoteAddress(), $action); } } /** * {@inheritDoc} */ public function afterController($controller, $methodName, Response $response) { if($this->reflector->hasAnnotation('BruteForceProtection') && $response->isThrottled()) { $action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action'); $ip = $this->request->getRemoteAddress(); $this->throttler->sleepDelay($ip, $action); $this->throttler->registerAttempt($action, $ip); } return parent::afterController($controller, $methodName, $response); } } private/AppFramework/Middleware/Security/Exceptions/SecurityException.php 0000604 00000002155 15247130452 0023022 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware\Security\Exceptions; /** * Class SecurityException is the base class for security exceptions thrown by * the security middleware. * * @package OC\AppFramework\Middleware\Security\Exceptions */ class SecurityException extends \Exception {} private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php 0000604 00000002403 15247130452 0025143 0 ustar 00 <?php /** * * @author Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\AppFramework\Middleware\Security\Exceptions; use OCP\AppFramework\Http; /** * Class StrictCookieMissingException is thrown when the strict cookie has not * been sent with the request but is required. * * @package OC\AppFramework\Middleware\Security\Exceptions */ class StrictCookieMissingException extends SecurityException { public function __construct() { parent::__construct('Strict Cookie has not been found in request.', Http::STATUS_PRECONDITION_FAILED); } } private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php 0000604 00000002504 15247130452 0023665 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware\Security\Exceptions; use OCP\AppFramework\Http; /** * Class AppNotEnabledException is thrown when a resource for an application is * requested that is not enabled. * * @package OC\AppFramework\Middleware\Security\Exceptions */ class AppNotEnabledException extends SecurityException { public function __construct() { parent::__construct('App is not enabled', Http::STATUS_PRECONDITION_FAILED); } } private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php 0000604 00000002476 15247130452 0026046 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware\Security\Exceptions; use OCP\AppFramework\Http; /** * Class CrossSiteRequestForgeryException is thrown when a CSRF exception has * been encountered. * * @package OC\AppFramework\Middleware\Security\Exceptions */ class CrossSiteRequestForgeryException extends SecurityException { public function __construct() { parent::__construct('CSRF check failed', Http::STATUS_PRECONDITION_FAILED); } } private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php 0000604 00000002555 15247130452 0022730 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware\Security\Exceptions; use OCP\AppFramework\Http; /** * Class NotAdminException is thrown when a resource has been requested by a * non-admin user that is not accessible to non-admin users. * * @package OC\AppFramework\Middleware\Security\Exceptions */ class NotAdminException extends SecurityException { public function __construct($message = 'Logged in user must be an admin') { parent::__construct($message, Http::STATUS_FORBIDDEN); } } private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php 0000604 00000002424 15247130452 0023601 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\AppFramework\Middleware\Security\Exceptions; use OCP\AppFramework\Http; /** * Class NotConfirmedException is thrown when a resource has been requested by a * user that has not confirmed their password in the last 30 minutes. * * @package OC\AppFramework\Middleware\Security\Exceptions */ class NotConfirmedException extends SecurityException { public function __construct() { parent::__construct('Password confirmation is required', Http::STATUS_FORBIDDEN); } } private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php 0000604 00000002530 15247130452 0023361 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware\Security\Exceptions; use OCP\AppFramework\Http; /** * Class NotLoggedInException is thrown when a resource has been requested by a * guest user that is not accessible to the public. * * @package OC\AppFramework\Middleware\Security\Exceptions */ class NotLoggedInException extends SecurityException { public function __construct() { parent::__construct('Current user is not logged in', Http::STATUS_UNAUTHORIZED); } } private/AppFramework/Middleware/Security/SecurityMiddleware.php 0000604 00000022704 15247130452 0021022 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException; use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException; use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException; use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException; use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException; use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException; use OC\AppFramework\Utility\ControllerMethodReflector; use OC\Security\CSP\ContentSecurityPolicyManager; use OC\Security\CSP\ContentSecurityPolicyNonceManager; use OC\Security\CSRF\CsrfTokenManager; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\EmptyContentSecurityPolicy; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Middleware; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\OCSController; use OCP\INavigationManager; use OCP\ISession; use OCP\IURLGenerator; use OCP\IRequest; use OCP\ILogger; use OCP\AppFramework\Controller; use OCP\Util; use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; /** * Used to do all the authentication and checking stuff for a controller method * It reads out the annotations of a controller method and checks which if * security things should be checked and also handles errors in case a security * check fails */ class SecurityMiddleware extends Middleware { /** @var INavigationManager */ private $navigationManager; /** @var IRequest */ private $request; /** @var ControllerMethodReflector */ private $reflector; /** @var string */ private $appName; /** @var IURLGenerator */ private $urlGenerator; /** @var ILogger */ private $logger; /** @var ISession */ private $session; /** @var bool */ private $isLoggedIn; /** @var bool */ private $isAdminUser; /** @var ContentSecurityPolicyManager */ private $contentSecurityPolicyManager; /** @var CsrfTokenManager */ private $csrfTokenManager; /** @var ContentSecurityPolicyNonceManager */ private $cspNonceManager; /** * @param IRequest $request * @param ControllerMethodReflector $reflector * @param INavigationManager $navigationManager * @param IURLGenerator $urlGenerator * @param ILogger $logger * @param ISession $session * @param string $appName * @param bool $isLoggedIn * @param bool $isAdminUser * @param ContentSecurityPolicyManager $contentSecurityPolicyManager * @param CSRFTokenManager $csrfTokenManager * @param ContentSecurityPolicyNonceManager $cspNonceManager */ public function __construct(IRequest $request, ControllerMethodReflector $reflector, INavigationManager $navigationManager, IURLGenerator $urlGenerator, ILogger $logger, ISession $session, $appName, $isLoggedIn, $isAdminUser, ContentSecurityPolicyManager $contentSecurityPolicyManager, CsrfTokenManager $csrfTokenManager, ContentSecurityPolicyNonceManager $cspNonceManager) { $this->navigationManager = $navigationManager; $this->request = $request; $this->reflector = $reflector; $this->appName = $appName; $this->urlGenerator = $urlGenerator; $this->logger = $logger; $this->session = $session; $this->isLoggedIn = $isLoggedIn; $this->isAdminUser = $isAdminUser; $this->contentSecurityPolicyManager = $contentSecurityPolicyManager; $this->csrfTokenManager = $csrfTokenManager; $this->cspNonceManager = $cspNonceManager; } /** * This runs all the security checks before a method call. The * security checks are determined by inspecting the controller method * annotations * @param Controller $controller the controller * @param string $methodName the name of the method * @throws SecurityException when a security check fails */ public function beforeController($controller, $methodName) { // this will set the current navigation entry of the app, use this only // for normal HTML requests and not for AJAX requests $this->navigationManager->setActiveEntry($this->appName); // security checks $isPublicPage = $this->reflector->hasAnnotation('PublicPage'); if(!$isPublicPage) { if(!$this->isLoggedIn) { throw new NotLoggedInException(); } if(!$this->reflector->hasAnnotation('NoAdminRequired')) { if(!$this->isAdminUser) { throw new NotAdminException(); } } } if ($this->reflector->hasAnnotation('PasswordConfirmationRequired')) { $lastConfirm = (int) $this->session->get('last-password-confirm'); if ($lastConfirm < (time() - (30 * 60 + 15))) { // allow 15 seconds delay throw new NotConfirmedException(); } } // Check for strict cookie requirement if($this->reflector->hasAnnotation('StrictCookieRequired') || !$this->reflector->hasAnnotation('NoCSRFRequired')) { if(!$this->request->passesStrictCookieCheck()) { throw new StrictCookieMissingException(); } } // CSRF check - also registers the CSRF token since the session may be closed later Util::callRegister(); if(!$this->reflector->hasAnnotation('NoCSRFRequired')) { /* * Only allow the CSRF check to fail on OCS Requests. This kind of * hacks around that we have no full token auth in place yet and we * do want to offer CSRF checks for web requests. */ if(!$this->request->passesCSRFCheck() && !( $controller instanceof OCSController && $this->request->getHeader('OCS-APIREQUEST') === 'true')) { throw new CrossSiteRequestForgeryException(); } } /** * FIXME: Use DI once available * Checks if app is enabled (also includes a check whether user is allowed to access the resource) * The getAppPath() check is here since components such as settings also use the AppFramework and * therefore won't pass this check. */ if(\OC_App::getAppPath($this->appName) !== false && !\OC_App::isEnabled($this->appName)) { throw new AppNotEnabledException(); } } /** * Performs the default CSP modifications that may be injected by other * applications * * @param Controller $controller * @param string $methodName * @param Response $response * @return Response */ public function afterController($controller, $methodName, Response $response) { $policy = !is_null($response->getContentSecurityPolicy()) ? $response->getContentSecurityPolicy() : new ContentSecurityPolicy(); if (get_class($policy) === EmptyContentSecurityPolicy::class) { return $response; } $defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy(); $defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy); if($this->cspNonceManager->browserSupportsCspV3()) { $defaultPolicy->useJsNonce($this->csrfTokenManager->getToken()->getEncryptedValue()); } $response->setContentSecurityPolicy($defaultPolicy); return $response; } /** * If an SecurityException is being caught, ajax requests return a JSON error * response and non ajax requests redirect to the index * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param \Exception $exception the thrown exception * @throws \Exception the passed in exception if it can't handle it * @return Response a Response object or null in case that the exception could not be handled */ public function afterException($controller, $methodName, \Exception $exception) { if($exception instanceof SecurityException) { if($exception instanceof StrictCookieMissingException) { return new RedirectResponse(\OC::$WEBROOT); } if (stripos($this->request->getHeader('Accept'),'html') === false) { $response = new JSONResponse( array('message' => $exception->getMessage()), $exception->getCode() ); } else { if($exception instanceof NotLoggedInException) { $params = []; if (isset($this->request->server['REQUEST_URI'])) { $params['redirect_url'] = $this->request->server['REQUEST_URI']; } $url = $this->urlGenerator->linkToRoute('core.login.showLoginForm', $params); $response = new RedirectResponse($url); } else { $response = new TemplateResponse('core', '403', ['file' => $exception->getMessage()], 'guest'); $response->setStatus($exception->getCode()); } } $this->logger->debug($exception->getMessage()); return $response; } throw $exception; } } private/AppFramework/Middleware/Security/RateLimitingMiddleware.php 0000604 00000007773 15247130452 0021614 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Utility\ControllerMethodReflector; use OC\Security\RateLimiting\Exception\RateLimitExceededException; use OC\Security\RateLimiting\Limiter; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Middleware; use OCP\IRequest; use OCP\IUserSession; /** * Class RateLimitingMiddleware is the middleware responsible for implementing the * ratelimiting in Nextcloud. * * It parses annotations such as: * * @UserRateThrottle(limit=5, period=100) * @AnonRateThrottle(limit=1, period=100) * * Those annotations above would mean that logged-in users can access the page 5 * times within 100 seconds, and anonymous users 1 time within 100 seconds. If * only an AnonRateThrottle is specified that one will also be applied to logged-in * users. * * @package OC\AppFramework\Middleware\Security */ class RateLimitingMiddleware extends Middleware { /** @var IRequest $request */ private $request; /** @var IUserSession */ private $userSession; /** @var ControllerMethodReflector */ private $reflector; /** @var Limiter */ private $limiter; /** * @param IRequest $request * @param IUserSession $userSession * @param ControllerMethodReflector $reflector * @param Limiter $limiter */ public function __construct(IRequest $request, IUserSession $userSession, ControllerMethodReflector $reflector, Limiter $limiter) { $this->request = $request; $this->userSession = $userSession; $this->reflector = $reflector; $this->limiter = $limiter; } /** * {@inheritDoc} * @throws RateLimitExceededException */ public function beforeController($controller, $methodName) { parent::beforeController($controller, $methodName); $anonLimit = $this->reflector->getAnnotationParameter('AnonRateThrottle', 'limit'); $anonPeriod = $this->reflector->getAnnotationParameter('AnonRateThrottle', 'period'); $userLimit = $this->reflector->getAnnotationParameter('UserRateThrottle', 'limit'); $userPeriod = $this->reflector->getAnnotationParameter('UserRateThrottle', 'period'); $rateLimitIdentifier = get_class($controller) . '::' . $methodName; if($userLimit !== '' && $userPeriod !== '' && $this->userSession->isLoggedIn()) { $this->limiter->registerUserRequest( $rateLimitIdentifier, $userLimit, $userPeriod, $this->userSession->getUser() ); } elseif ($anonLimit !== '' && $anonPeriod !== '') { $this->limiter->registerAnonRequest( $rateLimitIdentifier, $anonLimit, $anonPeriod, $this->request->getRemoteAddress() ); } } /** * {@inheritDoc} */ public function afterException($controller, $methodName, \Exception $exception) { if($exception instanceof RateLimitExceededException) { if (stripos($this->request->getHeader('Accept'),'html') === false) { $response = new JSONResponse( [ 'message' => $exception->getMessage(), ], $exception->getCode() ); } else { $response = new TemplateResponse( 'core', '403', [ 'file' => $exception->getMessage() ], 'guest' ); $response->setStatus($exception->getCode()); } return $response; } throw $exception; } } private/AppFramework/Middleware/Security/CORSMiddleware.php 0000604 00000013245 15247130452 0017761 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Christoph Wurst <christoph@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; use OC\AppFramework\Utility\ControllerMethodReflector; use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Security\Bruteforce\Throttler; use OC\User\Session; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; use OCP\IRequest; /** * This middleware sets the correct CORS headers on a response if the * controller has the @CORS annotation. This is needed for webapps that want * to access an API and don't run on the same domain, see * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS */ class CORSMiddleware extends Middleware { /** @var IRequest */ private $request; /** @var ControllerMethodReflector */ private $reflector; /** @var Session */ private $session; /** @var Throttler */ private $throttler; /** * @param IRequest $request * @param ControllerMethodReflector $reflector * @param Session $session * @param Throttler $throttler */ public function __construct(IRequest $request, ControllerMethodReflector $reflector, Session $session, Throttler $throttler) { $this->request = $request; $this->reflector = $reflector; $this->session = $session; $this->throttler = $throttler; } /** * This is being run in normal order before the controller is being * called which allows several modifications and checks * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @throws SecurityException * @since 6.0.0 */ public function beforeController($controller, $methodName){ // ensure that @CORS annotated API routes are not used in conjunction // with session authentication since this enables CSRF attack vectors if ($this->reflector->hasAnnotation('CORS') && !$this->reflector->hasAnnotation('PublicPage')) { $user = $this->request->server['PHP_AUTH_USER']; $pass = $this->request->server['PHP_AUTH_PW']; $this->session->logout(); try { if (!$this->session->logClientIn($user, $pass, $this->request, $this->throttler)) { throw new SecurityException('CORS requires basic auth', Http::STATUS_UNAUTHORIZED); } } catch (PasswordLoginForbiddenException $ex) { throw new SecurityException('Password login forbidden, use token instead', Http::STATUS_UNAUTHORIZED); } } } /** * This is being run after a successful controllermethod call and allows * the manipulation of a Response object. The middleware is run in reverse order * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param Response $response the generated response from the controller * @return Response a Response object * @throws SecurityException */ public function afterController($controller, $methodName, Response $response){ // only react if its a CORS request and if the request sends origin and if(isset($this->request->server['HTTP_ORIGIN']) && $this->reflector->hasAnnotation('CORS')) { // allow credentials headers must not be true or CSRF is possible // otherwise foreach($response->getHeaders() as $header => $value) { if(strtolower($header) === 'access-control-allow-credentials' && strtolower(trim($value)) === 'true') { $msg = 'Access-Control-Allow-Credentials must not be '. 'set to true in order to prevent CSRF'; throw new SecurityException($msg); } } $origin = $this->request->server['HTTP_ORIGIN']; $response->addHeader('Access-Control-Allow-Origin', $origin); } return $response; } /** * If an SecurityException is being caught return a JSON error response * * @param Controller $controller the controller that is being called * @param string $methodName the name of the method that will be called on * the controller * @param \Exception $exception the thrown exception * @throws \Exception the passed in exception if it can't handle it * @return Response a Response object or null in case that the exception could not be handled */ public function afterException($controller, $methodName, \Exception $exception){ if($exception instanceof SecurityException){ $response = new JSONResponse(['message' => $exception->getMessage()]); if($exception->getCode() !== 0) { $response->setStatus($exception->getCode()); } else { $response->setStatus(Http::STATUS_INTERNAL_SERVER_ERROR); } return $response; } throw $exception; } } private/AppFramework/DependencyInjection/DIContainer.php 0000604 00000031006 15247130452 0017404 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Christoph Wurst <christoph@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\DependencyInjection; use OC; use OC\AppFramework\Core\API; use OC\AppFramework\Http; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Http\Output; use OC\AppFramework\Middleware\MiddlewareDispatcher; use OC\AppFramework\Middleware\Security\CORSMiddleware; use OC\AppFramework\Middleware\OCSMiddleware; use OC\AppFramework\Middleware\Security\RateLimitingMiddleware; use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Middleware\SessionMiddleware; use OC\AppFramework\Utility\SimpleContainer; use OC\Core\Middleware\TwoFactorMiddleware; use OC\RichObjectStrings\Validator; use OC\ServerContainer; use OCP\AppFramework\Http\IOutput; use OCP\AppFramework\IApi; use OCP\AppFramework\IAppContainer; use OCP\AppFramework\QueryException; use OCP\Files\Folder; use OCP\Files\IAppData; use OCP\GlobalScale\IConfig; use OCP\IL10N; use OCP\IRequest; use OCP\IServerContainer; use OCP\IUserSession; use OCP\RichObjectStrings\IValidator; use OCP\Util; class DIContainer extends SimpleContainer implements IAppContainer { /** * @var array */ private $middleWares = array(); /** @var ServerContainer */ private $server; /** * Put your class dependencies in here * @param string $appName the name of the app * @param array $urlParams * @param ServerContainer $server */ public function __construct($appName, $urlParams = array(), ServerContainer $server = null){ parent::__construct(); $this['AppName'] = $appName; $this['urlParams'] = $urlParams; /** @var \OC\ServerContainer $server */ if ($server === null) { $server = \OC::$server; } $this->server = $server; $this->server->registerAppContainer($appName, $this); // aliases $this->registerAlias('appName', 'AppName'); $this->registerAlias('webRoot', 'WebRoot'); $this->registerAlias('userId', 'UserId'); /** * Core services */ $this->registerService(IOutput::class, function($c){ return new Output($this->getServer()->getWebRoot()); }); $this->registerService(Folder::class, function() { return $this->getServer()->getUserFolder(); }); $this->registerService(IAppData::class, function (SimpleContainer $c) { return $this->getServer()->getAppDataDir($c->query('AppName')); }); $this->registerService(IL10N::class, function($c) { return $this->getServer()->getL10N($c->query('AppName')); }); $this->registerAlias(\OCP\AppFramework\Utility\IControllerMethodReflector::class, \OC\AppFramework\Utility\ControllerMethodReflector::class); $this->registerAlias('ControllerMethodReflector', \OCP\AppFramework\Utility\IControllerMethodReflector::class); $this->registerService(IRequest::class, function() { return $this->getServer()->query(IRequest::class); }); $this->registerAlias('Request', IRequest::class); $this->registerAlias(\OCP\AppFramework\Utility\ITimeFactory::class, \OC\AppFramework\Utility\TimeFactory::class); $this->registerAlias('TimeFactory', \OCP\AppFramework\Utility\ITimeFactory::class); $this->registerAlias(\OC\User\Session::class, \OCP\IUserSession::class); $this->registerService(IServerContainer::class, function ($c) { return $this->getServer(); }); $this->registerAlias('ServerContainer', IServerContainer::class); $this->registerService(\OCP\WorkflowEngine\IManager::class, function ($c) { return $c->query('OCA\WorkflowEngine\Manager'); }); $this->registerService(\OCP\AppFramework\IAppContainer::class, function ($c) { return $c; }); // commonly used attributes $this->registerService('UserId', function ($c) { return $c->query('OCP\\IUserSession')->getSession()->get('user_id'); }); $this->registerService('WebRoot', function ($c) { return $c->query('ServerContainer')->getWebRoot(); }); $this->registerService('fromMailAddress', function() { return Util::getDefaultEmailAddress('no-reply'); }); $this->registerService('OC_Defaults', function ($c) { return $c->getServer()->getThemingDefaults(); }); $this->registerService('OCP\Encryption\IManager', function ($c) { return $this->getServer()->getEncryptionManager(); }); $this->registerService(IConfig::class, function ($c) { return $c->query(OC\GlobalScale\Config::class); }); $this->registerService(IValidator::class, function($c) { return $c->query(Validator::class); }); $this->registerService(\OC\Security\IdentityProof\Manager::class, function ($c) { return new \OC\Security\IdentityProof\Manager( $this->getServer()->query(\OC\Files\AppData\Factory::class), $this->getServer()->getCrypto(), $this->getServer()->getConfig() ); }); /** * App Framework APIs */ $this->registerService('API', function($c){ $c->query('OCP\\ILogger')->debug( 'Accessing the API class is deprecated! Use the appropriate ' . 'services instead!' ); return new API($c['AppName']); }); $this->registerService('Protocol', function($c){ /** @var \OC\Server $server */ $server = $c->query('ServerContainer'); $protocol = $server->getRequest()->getHttpProtocol(); return new Http($_SERVER, $protocol); }); $this->registerService('Dispatcher', function($c) { return new Dispatcher( $c['Protocol'], $c['MiddlewareDispatcher'], $c['ControllerMethodReflector'], $c['Request'] ); }); /** * App Framework default arguments */ $this->registerParameter('corsMethods', 'PUT, POST, GET, DELETE, PATCH'); $this->registerParameter('corsAllowedHeaders', 'Authorization, Content-Type, Accept'); $this->registerParameter('corsMaxAge', 1728000); /** * Middleware */ $app = $this; $this->registerService('SecurityMiddleware', function($c) use ($app){ /** @var \OC\Server $server */ $server = $app->getServer(); return new SecurityMiddleware( $c['Request'], $c['ControllerMethodReflector'], $server->getNavigationManager(), $server->getURLGenerator(), $server->getLogger(), $server->getSession(), $c['AppName'], $app->isLoggedIn(), $app->isAdminUser(), $server->getContentSecurityPolicyManager(), $server->getCsrfTokenManager(), $server->getContentSecurityPolicyNonceManager() ); }); $this->registerService('BruteForceMiddleware', function($c) use ($app) { /** @var \OC\Server $server */ $server = $app->getServer(); return new OC\AppFramework\Middleware\Security\BruteForceMiddleware( $c['ControllerMethodReflector'], $server->getBruteForceThrottler(), $server->getRequest() ); }); $this->registerService('RateLimitingMiddleware', function($c) use ($app) { /** @var \OC\Server $server */ $server = $app->getServer(); return new RateLimitingMiddleware( $server->getRequest(), $server->getUserSession(), $c['ControllerMethodReflector'], $c->query(OC\Security\RateLimiting\Limiter::class) ); }); $this->registerService('CORSMiddleware', function($c) { return new CORSMiddleware( $c['Request'], $c['ControllerMethodReflector'], $c->query(IUserSession::class), $c->getServer()->getBruteForceThrottler() ); }); $this->registerService('SessionMiddleware', function($c) use ($app) { return new SessionMiddleware( $c['Request'], $c['ControllerMethodReflector'], $app->getServer()->getSession() ); }); $this->registerService('TwoFactorMiddleware', function (SimpleContainer $c) use ($app) { $twoFactorManager = $c->getServer()->getTwoFactorAuthManager(); $userSession = $app->getServer()->getUserSession(); $session = $app->getServer()->getSession(); $urlGenerator = $app->getServer()->getURLGenerator(); $reflector = $c['ControllerMethodReflector']; $request = $app->getServer()->getRequest(); return new TwoFactorMiddleware($twoFactorManager, $userSession, $session, $urlGenerator, $reflector, $request); }); $this->registerService('OCSMiddleware', function (SimpleContainer $c) { return new OCSMiddleware( $c['Request'] ); }); $middleWares = &$this->middleWares; $this->registerService('MiddlewareDispatcher', function($c) use (&$middleWares) { $dispatcher = new MiddlewareDispatcher(); $dispatcher->registerMiddleware($c['CORSMiddleware']); $dispatcher->registerMiddleware($c['OCSMiddleware']); $dispatcher->registerMiddleware($c['SecurityMiddleware']); $dispatcher->registerMiddleware($c['TwoFactorMiddleware']); $dispatcher->registerMiddleware($c['BruteForceMiddleware']); $dispatcher->registerMiddleware($c['RateLimitingMiddleware']); foreach($middleWares as $middleWare) { $dispatcher->registerMiddleware($c[$middleWare]); } $dispatcher->registerMiddleware($c['SessionMiddleware']); return $dispatcher; }); } /** * @deprecated implements only deprecated methods * @return IApi */ function getCoreApi() { return $this->query('API'); } /** * @return \OCP\IServerContainer */ function getServer() { return $this->server; } /** * @param string $middleWare * @return boolean|null */ function registerMiddleWare($middleWare) { array_push($this->middleWares, $middleWare); } /** * used to return the appname of the set application * @return string the name of your application */ function getAppName() { return $this->query('AppName'); } /** * @deprecated use IUserSession->isLoggedIn() * @return boolean */ function isLoggedIn() { return \OC::$server->getUserSession()->isLoggedIn(); } /** * @deprecated use IGroupManager->isAdmin($userId) * @return boolean */ function isAdminUser() { $uid = $this->getUserId(); return \OC_User::isAdminUser($uid); } private function getUserId() { return $this->getServer()->getSession()->get('user_id'); } /** * @deprecated use the ILogger instead * @param string $message * @param string $level * @return mixed */ function log($message, $level) { switch($level){ case 'debug': $level = \OCP\Util::DEBUG; break; case 'info': $level = \OCP\Util::INFO; break; case 'warn': $level = \OCP\Util::WARN; break; case 'fatal': $level = \OCP\Util::FATAL; break; default: $level = \OCP\Util::ERROR; break; } \OCP\Util::writeLog($this->getAppName(), $message, $level); } /** * Register a capability * * @param string $serviceName e.g. 'OCA\Files\Capabilities' */ public function registerCapability($serviceName) { $this->query('OC\CapabilitiesManager')->registerCapability(function() use ($serviceName) { return $this->query($serviceName); }); } /** * @param string $name * @return mixed * @throws QueryException if the query could not be resolved */ public function query($name) { try { return $this->queryNoFallback($name); } catch (QueryException $e) { return $this->getServer()->query($name); } } /** * @param string $name * @return mixed * @throws QueryException if the query could not be resolved */ public function queryNoFallback($name) { $name = $this->sanitizeName($name); if ($this->offsetExists($name)) { return parent::query($name); } else { if ($this['AppName'] === 'settings' && strpos($name, 'OC\\Settings\\') === 0) { return parent::query($name); } else if ($this['AppName'] === 'core' && strpos($name, 'OC\\Core\\') === 0) { return parent::query($name); } else if (strpos($name, \OC\AppFramework\App::buildAppNamespace($this['AppName']) . '\\') === 0) { return parent::query($name); } } throw new QueryException('Could not resolve ' . $name . '!' . ' Class can not be instantiated'); } } private/AppFramework/Core/API.php 0000604 00000013336 15247130452 0012633 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\AppFramework\Core; use OCP\AppFramework\IApi; /** * This is used to wrap the owncloud static api calls into an object to make the * code better abstractable for use in the dependency injection container * * Should you find yourself in need for more methods, simply inherit from this * class and add your methods * @deprecated */ class API implements IApi{ private $appName; /** * constructor * @param string $appName the name of your application */ public function __construct($appName){ $this->appName = $appName; } /** * Gets the userid of the current user * @return string the user id of the current user * @deprecated Use \OC::$server->getUserSession()->getUser()->getUID() */ public function getUserId(){ return \OCP\User::getUser(); } /** * Adds a new javascript file * @deprecated include javascript and css in template files * @param string $scriptName the name of the javascript in js/ without the suffix * @param string $appName the name of the app, defaults to the current one */ public function addScript($scriptName, $appName=null){ if($appName === null){ $appName = $this->appName; } \OCP\Util::addScript($appName, $scriptName); } /** * Adds a new css file * @deprecated include javascript and css in template files * @param string $styleName the name of the css file in css/without the suffix * @param string $appName the name of the app, defaults to the current one */ public function addStyle($styleName, $appName=null){ if($appName === null){ $appName = $this->appName; } \OCP\Util::addStyle($appName, $styleName); } /** * @deprecated include javascript and css in template files * shorthand for addScript for files in the 3rdparty directory * @param string $name the name of the file without the suffix */ public function add3rdPartyScript($name){ \OCP\Util::addScript($this->appName . '/3rdparty', $name); } /** * @deprecated include javascript and css in template files * shorthand for addStyle for files in the 3rdparty directory * @param string $name the name of the file without the suffix */ public function add3rdPartyStyle($name){ \OCP\Util::addStyle($this->appName . '/3rdparty', $name); } /** * @deprecated communication between apps should happen over built in * callbacks or interfaces (check the contacts and calendar managers) * Checks if an app is enabled * also use \OC::$server->getAppManager()->isEnabledForUser($appName) * @param string $appName the name of an app * @return bool true if app is enabled */ public function isAppEnabled($appName){ return \OCP\App::isEnabled($appName); } /** * used to return and open a new event source * @return \OCP\IEventSource a new open EventSource class * @deprecated Use \OC::$server->createEventSource(); */ public function openEventSource(){ return \OC::$server->createEventSource(); } /** * @deprecated register hooks directly for class that build in hook interfaces * connects a function to a hook * @param string $signalClass class name of emitter * @param string $signalName name of signal * @param string $slotClass class name of slot * @param string $slotName name of slot, in another word, this is the * name of the method that will be called when registered * signal is emitted. * @return bool always true */ public function connectHook($signalClass, $signalName, $slotClass, $slotName) { return \OCP\Util::connectHook($signalClass, $signalName, $slotClass, $slotName); } /** * @deprecated implement the emitter interface instead * Emits a signal. To get data from the slot use references! * @param string $signalClass class name of emitter * @param string $signalName name of signal * @param array $params default: array() array with additional data * @return bool true if slots exists or false if not */ public function emitHook($signalClass, $signalName, $params = array()) { return \OCP\Util::emitHook($signalClass, $signalName, $params); } /** * clear hooks * @deprecated clear hooks directly for class that build in hook interfaces * @param string $signalClass * @param string $signalName */ public function clearHook($signalClass=false, $signalName=false) { if ($signalClass) { \OC_Hook::clear($signalClass, $signalName); } } /** * Tells ownCloud to include a template in the admin overview * @param string $mainPath the path to the main php file without the php * suffix, relative to your apps directory! not the template directory * @param string $appName the name of the app, defaults to the current one */ public function registerAdmin($mainPath, $appName=null) { if($appName === null){ $appName = $this->appName; } \OCP\App::registerAdmin($appName, $mainPath); } } private/Search.php 0000604 00000007552 15247130452 0010144 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OCP\Search\PagedProvider; use OCP\Search\Provider; use OCP\ISearch; /** * Provide an interface to all search providers */ class Search implements ISearch { private $providers = array(); private $registeredProviders = array(); /** * Search all providers for $query * @param string $query * @param string[] $inApps optionally limit results to the given apps * @return array An array of OC\Search\Result's */ public function search($query, array $inApps = array()) { // old apps might assume they get all results, so we set size 0 return $this->searchPaged($query, $inApps, 1, 0); } /** * Search all providers for $query * @param string $query * @param string[] $inApps optionally limit results to the given apps * @param int $page pages start at page 1 * @param int $size, 0 = all * @return array An array of OC\Search\Result's */ public function searchPaged($query, array $inApps = array(), $page = 1, $size = 30) { $this->initProviders(); $results = array(); foreach($this->providers as $provider) { /** @var $provider Provider */ if ( ! $provider->providesResultsFor($inApps) ) { continue; } if ($provider instanceof PagedProvider) { $results = array_merge($results, $provider->searchPaged($query, $page, $size)); } else if ($provider instanceof Provider) { $providerResults = $provider->search($query); if ($size > 0) { $slicedResults = array_slice($providerResults, ($page - 1) * $size, $size); $results = array_merge($results, $slicedResults); } else { $results = array_merge($results, $providerResults); } } else { \OC::$server->getLogger()->warning('Ignoring Unknown search provider', array('provider' => $provider)); } } return $results; } /** * Remove all registered search providers */ public function clearProviders() { $this->providers = array(); $this->registeredProviders = array(); } /** * Remove one existing search provider * @param string $provider class name of a OC\Search\Provider */ public function removeProvider($provider) { $this->registeredProviders = array_filter( $this->registeredProviders, function ($element) use ($provider) { return ($element['class'] != $provider); } ); // force regeneration of providers on next search $this->providers = array(); } /** * Register a new search provider to search with * @param string $class class name of a OC\Search\Provider * @param array $options optional */ public function registerProvider($class, array $options = array()) { $this->registeredProviders[] = array('class' => $class, 'options' => $options); } /** * Create instances of all the registered search providers */ private function initProviders() { if( ! empty($this->providers) ) { return; } foreach($this->registeredProviders as $provider) { $class = $provider['class']; $options = $provider['options']; $this->providers[] = new $class($options); } } } private/Files/SimpleFS/SimpleFolder.php 0000604 00000004052 15247130452 0014040 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\SimpleFS; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFolder; class SimpleFolder implements ISimpleFolder { /** @var Folder */ private $folder; /** * Folder constructor. * * @param Folder $folder */ public function __construct(Folder $folder) { $this->folder = $folder; } public function getName() { return $this->folder->getName(); } public function getDirectoryListing() { $listing = $this->folder->getDirectoryListing(); $fileListing = array_map(function(Node $file) { if ($file instanceof File) { return new SimpleFile($file); } return null; }, $listing); $fileListing = array_filter($fileListing); return array_values($fileListing); } public function delete() { $this->folder->delete(); } public function fileExists($name) { return $this->folder->nodeExists($name); } public function getFile($name) { $file = $this->folder->get($name); if (!($file instanceof File)) { throw new NotFoundException(); } return new SimpleFile($file); } public function newFile($name) { $file = $this->folder->newFile($name); return new SimpleFile($file); } } private/Files/SimpleFS/SimpleFile.php 0000604 00000004266 15247130452 0013513 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\SimpleFS; use OCP\Files\File; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; class SimpleFile implements ISimpleFile { /** @var File $file */ private $file; /** * File constructor. * * @param File $file */ public function __construct(File $file) { $this->file = $file; } /** * Get the name * * @return string */ public function getName() { return $this->file->getName(); } /** * Get the size in bytes * * @return int */ public function getSize() { return $this->file->getSize(); } /** * Get the ETag * * @return string */ public function getETag() { return $this->file->getEtag(); } /** * Get the last modification time * * @return int */ public function getMTime() { return $this->file->getMTime(); } /** * Get the content * * @return string */ public function getContent() { return $this->file->getContent(); } /** * Overwrite the file * * @param string $data * @throws NotPermittedException */ public function putContent($data) { $this->file->putContent($data); } /** * Delete the file * * @throws NotPermittedException */ public function delete() { $this->file->delete(); } /** * Get the MimeType * * @return string */ public function getMimeType() { return $this->file->getMimeType(); } } private/Files/Notify/RenameChange.php 0000604 00000002645 15247130452 0013564 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\Notify; use OCP\Files\Notify\IRenameChange; class RenameChange extends Change implements IRenameChange { /** @var string */ private $targetPath; /** * Change constructor. * * @param int $type * @param string $path * @param string $targetPath */ public function __construct($type, $path, $targetPath) { parent::__construct($type, $path); $this->targetPath = $targetPath; } /** * Get the new path of the renamed file relative to the storage root * * @return string */ public function getTargetPath() { return $this->targetPath; } } private/Files/Notify/Change.php 0000604 00000003136 15247130452 0012430 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\Notify; use OCP\Files\Notify\IChange; class Change implements IChange { /** @var int */ private $type; /** @var string */ private $path; /** * Change constructor. * * @param int $type * @param string $path */ public function __construct($type, $path) { $this->type = $type; $this->path = $path; } /** * Get the type of the change * * @return int IChange::ADDED, IChange::REMOVED, IChange::MODIFIED or IChange::RENAMED */ public function getType() { return $this->type; } /** * Get the path of the file that was changed relative to the root of the storage * * Note, for rename changes this path is the old path for the file * * @return mixed */ public function getPath() { return $this->path; } } private/Files/AppData/Factory.php 0000604 00000002525 15247130452 0012715 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\AppData; use OC\SystemConfig; use OCP\Files\IRootFolder; class Factory { /** @var IRootFolder */ private $rootFolder; /** @var SystemConfig */ private $config; public function __construct(IRootFolder $rootFolder, SystemConfig $systemConfig) { $this->rootFolder = $rootFolder; $this->config = $systemConfig; } /** * @param string $appId * @return AppData */ public function get($appId) { return new AppData($this->rootFolder, $this->config, $appId); } } private/Files/AppData/AppData.php 0000604 00000006325 15247130452 0012622 0 ustar 00 <?php /** * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\AppData; use OC\Files\SimpleFS\SimpleFolder; use OCP\Files\IAppData; use OCP\Files\IRootFolder; use OCP\Files\Folder; use OC\SystemConfig; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; class AppData implements IAppData { /** @var IRootFolder */ private $rootFolder; /** @var SystemConfig */ private $config; /** @var string */ private $appId; /** @var Folder */ private $folder; /** * AppData constructor. * * @param IRootFolder $rootFolder * @param SystemConfig $systemConfig * @param string $appId */ public function __construct(IRootFolder $rootFolder, SystemConfig $systemConfig, $appId) { $this->rootFolder = $rootFolder; $this->config = $systemConfig; $this->appId = $appId; } /** * @return Folder * @throws \RuntimeException */ private function getAppDataFolder() { if ($this->folder === null) { $instanceId = $this->config->getValue('instanceid', null); if ($instanceId === null) { throw new \RuntimeException('no instance id!'); } $name = 'appdata_' . $instanceId; try { $appDataFolder = $this->rootFolder->get($name); } catch (NotFoundException $e) { try { $appDataFolder = $this->rootFolder->newFolder($name); } catch (NotPermittedException $e) { throw new \RuntimeException('Could not get appdata folder'); } } try { $appDataFolder = $appDataFolder->get($this->appId); } catch (NotFoundException $e) { try { $appDataFolder = $appDataFolder->newFolder($this->appId); } catch (NotPermittedException $e) { throw new \RuntimeException('Could not get appdata folder for ' . $this->appId); } } $this->folder = $appDataFolder; } return $this->folder; } public function getFolder($name) { $node = $this->getAppDataFolder()->get($name); /** @var Folder $node */ return new SimpleFolder($node); } public function newFolder($name) { $folder = $this->getAppDataFolder()->newFolder($name); return new SimpleFolder($folder); } public function getDirectoryListing() { $listing = $this->getAppDataFolder()->getDirectoryListing(); $fileListing = array_map(function(Node $folder) { if ($folder instanceof Folder) { return new SimpleFolder($folder); } return null; }, $listing); $fileListing = array_filter($fileListing); return array_values($fileListing); } } private/Files/Storage/Common.php 0000604 00000051255 15247130452 0012634 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author hkjolhede <hkjolhede@gmail.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Martin Mattel <martin.mattel@diemattels.at> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sam Tuke <mail@samtuke.com> * @author scambra <sergio@entrecables.com> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; use OC\Files\Cache\Cache; use OC\Files\Cache\Propagator; use OC\Files\Cache\Scanner; use OC\Files\Cache\Updater; use OC\Files\Filesystem; use OC\Files\Cache\Watcher; use OCP\Files\EmptyFileNameException; use OCP\Files\FileNameTooLongException; use OCP\Files\InvalidCharacterInPathException; use OCP\Files\InvalidDirectoryException; use OCP\Files\InvalidPathException; use OCP\Files\ReservedWordException; use OCP\Files\Storage\ILockingStorage; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; /** * Storage backend class for providing common filesystem operation methods * which are not storage-backend specific. * * \OC\Files\Storage\Common is never used directly; it is extended by all other * storage backends, where its methods may be overridden, and additional * (backend-specific) methods are defined. * * Some \OC\Files\Storage\Common methods call functions which are first defined * in classes which extend it, e.g. $this->stat() . */ abstract class Common implements Storage, ILockingStorage { use LocalTempFileTrait; protected $cache; protected $scanner; protected $watcher; protected $propagator; protected $storageCache; protected $updater; protected $mountOptions = []; protected $owner = null; private $shouldLogLocks = null; private $logger; public function __construct($parameters) { } /** * Remove a file or folder * * @param string $path * @return bool */ protected function remove($path) { if ($this->is_dir($path)) { return $this->rmdir($path); } else if ($this->is_file($path)) { return $this->unlink($path); } else { return false; } } public function is_dir($path) { return $this->filetype($path) === 'dir'; } public function is_file($path) { return $this->filetype($path) === 'file'; } public function filesize($path) { if ($this->is_dir($path)) { return 0; //by definition } else { $stat = $this->stat($path); if (isset($stat['size'])) { return $stat['size']; } else { return 0; } } } public function isReadable($path) { // at least check whether it exists // subclasses might want to implement this more thoroughly return $this->file_exists($path); } public function isUpdatable($path) { // at least check whether it exists // subclasses might want to implement this more thoroughly // a non-existing file/folder isn't updatable return $this->file_exists($path); } public function isCreatable($path) { if ($this->is_dir($path) && $this->isUpdatable($path)) { return true; } return false; } public function isDeletable($path) { if ($path === '' || $path === '/') { return false; } $parent = dirname($path); return $this->isUpdatable($parent) && $this->isUpdatable($path); } public function isSharable($path) { return $this->isReadable($path); } public function getPermissions($path) { $permissions = 0; if ($this->isCreatable($path)) { $permissions |= \OCP\Constants::PERMISSION_CREATE; } if ($this->isReadable($path)) { $permissions |= \OCP\Constants::PERMISSION_READ; } if ($this->isUpdatable($path)) { $permissions |= \OCP\Constants::PERMISSION_UPDATE; } if ($this->isDeletable($path)) { $permissions |= \OCP\Constants::PERMISSION_DELETE; } if ($this->isSharable($path)) { $permissions |= \OCP\Constants::PERMISSION_SHARE; } return $permissions; } public function filemtime($path) { $stat = $this->stat($path); if (isset($stat['mtime']) && $stat['mtime'] > 0) { return $stat['mtime']; } else { return 0; } } public function file_get_contents($path) { $handle = $this->fopen($path, "r"); if (!$handle) { return false; } $data = stream_get_contents($handle); fclose($handle); return $data; } public function file_put_contents($path, $data) { $handle = $this->fopen($path, "w"); $this->removeCachedFile($path); $count = fwrite($handle, $data); fclose($handle); return $count; } public function rename($path1, $path2) { $this->remove($path2); $this->removeCachedFile($path1); return $this->copy($path1, $path2) and $this->remove($path1); } public function copy($path1, $path2) { if ($this->is_dir($path1)) { $this->remove($path2); $dir = $this->opendir($path1); $this->mkdir($path2); while ($file = readdir($dir)) { if (!Filesystem::isIgnoredDir($file)) { if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) { return false; } } } closedir($dir); return true; } else { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); $this->removeCachedFile($path2); return $result; } } public function getMimeType($path) { if ($this->is_dir($path)) { return 'httpd/unix-directory'; } elseif ($this->file_exists($path)) { return \OC::$server->getMimeTypeDetector()->detectPath($path); } else { return false; } } public function hash($type, $path, $raw = false) { $fh = $this->fopen($path, 'rb'); $ctx = hash_init($type); hash_update_stream($ctx, $fh); fclose($fh); return hash_final($ctx, $raw); } public function search($query) { return $this->searchInDir($query); } public function getLocalFile($path) { return $this->getCachedFile($path); } /** * @param string $path * @param string $target */ private function addLocalFolder($path, $target) { $dh = $this->opendir($path); if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { if (!\OC\Files\Filesystem::isIgnoredDir($file)) { if ($this->is_dir($path . '/' . $file)) { mkdir($target . '/' . $file); $this->addLocalFolder($path . '/' . $file, $target . '/' . $file); } else { $tmp = $this->toTmpFile($path . '/' . $file); rename($tmp, $target . '/' . $file); } } } } } /** * @param string $query * @param string $dir * @return array */ protected function searchInDir($query, $dir = '') { $files = array(); $dh = $this->opendir($dir); if (is_resource($dh)) { while (($item = readdir($dh)) !== false) { if (\OC\Files\Filesystem::isIgnoredDir($item)) continue; if (strstr(strtolower($item), strtolower($query)) !== false) { $files[] = $dir . '/' . $item; } if ($this->is_dir($dir . '/' . $item)) { $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item)); } } } closedir($dh); return $files; } /** * check if a file or folder has been updated since $time * * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking * the mtime should always return false here. As a result storage implementations that always return false expect * exclusive access to the backend and will not pick up files that have been added in a way that circumvents * ownClouds filesystem. * * @param string $path * @param int $time * @return bool */ public function hasUpdated($path, $time) { return $this->filemtime($path) > $time; } public function getCache($path = '', $storage = null) { if (!$storage) { $storage = $this; } if (!isset($storage->cache)) { $storage->cache = new Cache($storage); } return $storage->cache; } public function getScanner($path = '', $storage = null) { if (!$storage) { $storage = $this; } if (!isset($storage->scanner)) { $storage->scanner = new Scanner($storage); } return $storage->scanner; } public function getWatcher($path = '', $storage = null) { if (!$storage) { $storage = $this; } if (!isset($this->watcher)) { $this->watcher = new Watcher($storage); $globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER); $this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy)); } return $this->watcher; } /** * get a propagator instance for the cache * * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Propagator */ public function getPropagator($storage = null) { if (!$storage) { $storage = $this; } if (!isset($storage->propagator)) { $storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection()); } return $storage->propagator; } public function getUpdater($storage = null) { if (!$storage) { $storage = $this; } if (!isset($storage->updater)) { $storage->updater = new Updater($storage); } return $storage->updater; } public function getStorageCache($storage = null) { if (!$storage) { $storage = $this; } if (!isset($this->storageCache)) { $this->storageCache = new \OC\Files\Cache\Storage($storage); } return $this->storageCache; } /** * get the owner of a path * * @param string $path The path to get the owner * @return string|false uid or false */ public function getOwner($path) { if ($this->owner === null) { $this->owner = \OC_User::getUser(); } return $this->owner; } /** * get the ETag for a file or folder * * @param string $path * @return string */ public function getETag($path) { return uniqid(); } /** * clean a path, i.e. remove all redundant '.' and '..' * making sure that it can't point to higher than '/' * * @param string $path The path to clean * @return string cleaned path */ public function cleanPath($path) { if (strlen($path) == 0 or $path[0] != '/') { $path = '/' . $path; } $output = array(); foreach (explode('/', $path) as $chunk) { if ($chunk == '..') { array_pop($output); } else if ($chunk == '.') { } else { $output[] = $chunk; } } return implode('/', $output); } /** * Test a storage for availability * * @return bool */ public function test() { try { if ($this->stat('')) { return true; } return false; } catch (\Exception $e) { return false; } } /** * get the free space in the storage * * @param string $path * @return int|false */ public function free_space($path) { return \OCP\Files\FileInfo::SPACE_UNKNOWN; } /** * {@inheritdoc} */ public function isLocal() { // the common implementation returns a temporary file by // default, which is not local return false; } /** * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class * * @param string $class * @return bool */ public function instanceOfStorage($class) { if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') { // FIXME Temporary fix to keep existing checks working $class = '\OCA\Files_Sharing\SharedStorage'; } return is_a($this, $class); } /** * A custom storage implementation can return an url for direct download of a give file. * * For now the returned array can hold the parameter url - in future more attributes might follow. * * @param string $path * @return array|false */ public function getDirectDownload($path) { return []; } /** * @inheritdoc * @throws InvalidPathException */ public function verifyPath($path, $fileName) { // verify empty and dot files $trimmed = trim($fileName); if ($trimmed === '') { throw new EmptyFileNameException(); } if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) { throw new InvalidDirectoryException(); } if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) { // verify database - e.g. mysql only 3-byte chars if (preg_match('%(?: \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 )%xs', $fileName)) { throw new InvalidCharacterInPathException(); } } if (isset($fileName[255])) { throw new FileNameTooLongException(); } // NOTE: $path will remain unverified for now $this->verifyPosixPath($fileName); } /** * @param string $fileName * @throws InvalidPathException */ protected function verifyPosixPath($fileName) { $fileName = trim($fileName); $this->scanForInvalidCharacters($fileName, "\\/"); $reservedNames = ['*']; if (in_array($fileName, $reservedNames)) { throw new ReservedWordException(); } } /** * @param string $fileName * @param string $invalidChars * @throws InvalidPathException */ private function scanForInvalidCharacters($fileName, $invalidChars) { foreach (str_split($invalidChars) as $char) { if (strpos($fileName, $char) !== false) { throw new InvalidCharacterInPathException(); } } $sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW); if ($sanitizedFileName !== $fileName) { throw new InvalidCharacterInPathException(); } } /** * @param array $options */ public function setMountOptions(array $options) { $this->mountOptions = $options; } /** * @param string $name * @param mixed $default * @return mixed */ public function getMountOption($name, $default = null) { return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default; } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @param bool $preserveMtime * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) { if ($sourceStorage === $this) { return $this->copy($sourceInternalPath, $targetInternalPath); } if ($sourceStorage->is_dir($sourceInternalPath)) { $dh = $sourceStorage->opendir($sourceInternalPath); $result = $this->mkdir($targetInternalPath); if (is_resource($dh)) { while ($result and ($file = readdir($dh)) !== false) { if (!Filesystem::isIgnoredDir($file)) { $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file); } } } } else { $source = $sourceStorage->fopen($sourceInternalPath, 'r'); // TODO: call fopen in a way that we execute again all storage wrappers // to avoid that we bypass storage wrappers which perform important actions // for this operation. Same is true for all other operations which // are not the same as the original one.Once this is fixed we also // need to adjust the encryption wrapper. $target = $this->fopen($targetInternalPath, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); if ($result and $preserveMtime) { $this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath)); } fclose($source); fclose($target); if (!$result) { // delete partially written target file $this->unlink($targetInternalPath); // delete cache entry that was created by fopen $this->getCache()->remove($targetInternalPath); } } return (bool)$result; } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { if ($sourceStorage === $this) { return $this->rename($sourceInternalPath, $targetInternalPath); } if (!$sourceStorage->isDeletable($sourceInternalPath)) { return false; } $result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true); if ($result) { if ($sourceStorage->is_dir($sourceInternalPath)) { $result &= $sourceStorage->rmdir($sourceInternalPath); } else { $result &= $sourceStorage->unlink($sourceInternalPath); } } return $result; } /** * @inheritdoc */ public function getMetaData($path) { $permissions = $this->getPermissions($path); if (!$permissions & \OCP\Constants::PERMISSION_READ) { //can't read, nothing we can do return null; } $data = []; $data['mimetype'] = $this->getMimeType($path); $data['mtime'] = $this->filemtime($path); if ($data['mtime'] === false) { $data['mtime'] = time(); } if ($data['mimetype'] == 'httpd/unix-directory') { $data['size'] = -1; //unknown } else { $data['size'] = $this->filesize($path); } $data['etag'] = $this->getETag($path); $data['storage_mtime'] = $data['mtime']; $data['permissions'] = $permissions; return $data; } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException */ public function acquireLock($path, $type, ILockingProvider $provider) { $logger = $this->getLockLogger(); if ($logger) { $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive'; $logger->info( sprintf( 'acquire %s lock on "%s" on storage "%s"', $typeString, $path, $this->getId() ), [ 'app' => 'locking', ] ); } try { $provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type); } catch (LockedException $e) { if ($logger) { $logger->logException($e); } throw $e; } } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException */ public function releaseLock($path, $type, ILockingProvider $provider) { $logger = $this->getLockLogger(); if ($logger) { $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive'; $logger->info( sprintf( 'release %s lock on "%s" on storage "%s"', $typeString, $path, $this->getId() ), [ 'app' => 'locking', ] ); } try { $provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type); } catch (LockedException $e) { if ($logger) { $logger->logException($e); } throw $e; } } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException */ public function changeLock($path, $type, ILockingProvider $provider) { $logger = $this->getLockLogger(); if ($logger) { $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive'; $logger->info( sprintf( 'change lock on "%s" to %s on storage "%s"', $path, $typeString, $this->getId() ), [ 'app' => 'locking', ] ); } try { $provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type); } catch (LockedException $e) { if ($logger) { $logger->logException($e); } throw $e; } } private function getLockLogger() { if (is_null($this->shouldLogLocks)) { $this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false); $this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null; } return $this->logger; } /** * @return array [ available, last_checked ] */ public function getAvailability() { return $this->getStorageCache()->getAvailability(); } /** * @param bool $isAvailable */ public function setAvailability($isAvailable) { $this->getStorageCache()->setAvailability($isAvailable); } /** * @return bool */ public function needsPartFile() { return true; } } private/Files/Storage/LocalTempFileTrait.php 0000604 00000004377 15247130452 0015073 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; /** * Storage backend class for providing common filesystem operation methods * which are not storage-backend specific. * * \OC\Files\Storage\Common is never used directly; it is extended by all other * storage backends, where its methods may be overridden, and additional * (backend-specific) methods are defined. * * Some \OC\Files\Storage\Common methods call functions which are first defined * in classes which extend it, e.g. $this->stat() . */ trait LocalTempFileTrait { /** @var string[] */ protected $cachedFiles = []; /** * @param string $path * @return string */ protected function getCachedFile($path) { if (!isset($this->cachedFiles[$path])) { $this->cachedFiles[$path] = $this->toTmpFile($path); } return $this->cachedFiles[$path]; } /** * @param string $path */ protected function removeCachedFile($path) { unset($this->cachedFiles[$path]); } /** * @param string $path * @return string */ protected function toTmpFile($path) { //no longer in the storage api, still useful here $source = $this->fopen($path, 'r'); if (!$source) { return false; } if ($pos = strrpos($path, '.')) { $extension = substr($path, $pos); } else { $extension = ''; } $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension); $target = fopen($tmpFile, 'w'); \OC_Helper::streamCopy($source, $target); fclose($target); return $tmpFile; } } private/Files/Storage/Storage.php 0000604 00000007166 15247130452 0013012 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; use OCP\Lock\ILockingProvider; /** * Provide a common interface to all different storage options * * All paths passed to the storage are relative to the storage and should NOT have a leading slash. */ interface Storage extends \OCP\Files\Storage { /** * get a cache instance for the storage * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache * @return \OC\Files\Cache\Cache */ public function getCache($path = '', $storage = null); /** * get a scanner instance for the storage * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner * @return \OC\Files\Cache\Scanner */ public function getScanner($path = '', $storage = null); /** * get the user id of the owner of a file or folder * * @param string $path * @return string */ public function getOwner($path); /** * get a watcher instance for the cache * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Watcher */ public function getWatcher($path = '', $storage = null); /** * get a propagator instance for the cache * * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Propagator */ public function getPropagator($storage = null); /** * get a updater instance for the cache * * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Updater */ public function getUpdater($storage = null); /** * @return \OC\Files\Cache\Storage */ public function getStorageCache(); /** * @param string $path * @return array */ public function getMetaData($path); /** * @param string $path The path of the file to acquire the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException */ public function acquireLock($path, $type, ILockingProvider $provider); /** * @param string $path The path of the file to release the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException */ public function releaseLock($path, $type, ILockingProvider $provider); /** * @param string $path The path of the file to change the lock for * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException */ public function changeLock($path, $type, ILockingProvider $provider); } private/Files/Storage/PolyFill/CopyDirectory.php 0000604 00000004722 15247130452 0015732 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Martin Mattel <martin.mattel@diemattels.at> * @author Robin Appelman <robin@icewind.nl> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage\PolyFill; trait CopyDirectory { /** * Check if a path is a directory * * @param string $path * @return bool */ abstract public function is_dir($path); /** * Check if a file or folder exists * * @param string $path * @return bool */ abstract public function file_exists($path); /** * Delete a file or folder * * @param string $path * @return bool */ abstract public function unlink($path); /** * Open a directory handle for a folder * * @param string $path * @return resource | bool */ abstract public function opendir($path); /** * Create a new folder * * @param string $path * @return bool */ abstract public function mkdir($path); public function copy($source, $target) { if ($this->is_dir($source)) { if ($this->file_exists($target)) { $this->unlink($target); } $this->mkdir($target); return $this->copyRecursive($source, $target); } else { return parent::copy($source, $target); } } /** * For adapters that don't support copying folders natively * * @param $source * @param $target * @return bool */ protected function copyRecursive($source, $target) { $dh = $this->opendir($source); $result = true; while ($file = readdir($dh)) { if (!\OC\Files\Filesystem::isIgnoredDir($file)) { if ($this->is_dir($source . '/' . $file)) { $this->mkdir($target . '/' . $file); $result = $this->copyRecursive($source . '/' . $file, $target . '/' . $file); } else { $result = parent::copy($source . '/' . $file, $target . '/' . $file); } if (!$result) { break; } } } return $result; } } private/Files/Storage/Local.php 0000604 00000030403 15247130452 0012426 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Brice Maron <brice@bmaron.net> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Klaas Freitag <freitag@owncloud.com> * @author Martin Mattel <martin.mattel@diemattels.at> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Sjors van der Pluijm <sjors@desjors.nl> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; use OC\Files\Storage\Wrapper\Jail; use OCP\Files\ForbiddenException; /** * for local filestore, we only have to map the paths */ class Local extends \OC\Files\Storage\Common { protected $datadir; protected $dataDirLength; protected $allowSymlinks = false; protected $realDataDir; public function __construct($arguments) { if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) { throw new \InvalidArgumentException('No data directory set for local storage'); } $this->datadir = $arguments['datadir']; // some crazy code uses a local storage on root... if ($this->datadir === '/') { $this->realDataDir = $this->datadir; } else { $this->realDataDir = rtrim(realpath($this->datadir), '/') . '/'; } if (substr($this->datadir, -1) !== '/') { $this->datadir .= '/'; } $this->dataDirLength = strlen($this->realDataDir); } public function __destruct() { } public function getId() { return 'local::' . $this->datadir; } public function mkdir($path) { return @mkdir($this->getSourcePath($path), 0777, true); } public function rmdir($path) { if (!$this->isDeletable($path)) { return false; } try { $it = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($this->getSourcePath($path)), \RecursiveIteratorIterator::CHILD_FIRST ); /** * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach * This bug is fixed in PHP 5.5.9 or before * See #8376 */ $it->rewind(); while ($it->valid()) { /** * @var \SplFileInfo $file */ $file = $it->current(); if (in_array($file->getBasename(), array('.', '..'))) { $it->next(); continue; } elseif ($file->isDir()) { rmdir($file->getPathname()); } elseif ($file->isFile() || $file->isLink()) { unlink($file->getPathname()); } $it->next(); } return rmdir($this->getSourcePath($path)); } catch (\UnexpectedValueException $e) { return false; } } public function opendir($path) { return opendir($this->getSourcePath($path)); } public function is_dir($path) { if (substr($path, -1) == '/') { $path = substr($path, 0, -1); } return is_dir($this->getSourcePath($path)); } public function is_file($path) { return is_file($this->getSourcePath($path)); } public function stat($path) { clearstatcache(); $fullPath = $this->getSourcePath($path); $statResult = stat($fullPath); if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) { $filesize = $this->filesize($path); $statResult['size'] = $filesize; $statResult[7] = $filesize; } return $statResult; } public function filetype($path) { $filetype = filetype($this->getSourcePath($path)); if ($filetype == 'link') { $filetype = filetype(realpath($this->getSourcePath($path))); } return $filetype; } public function filesize($path) { if ($this->is_dir($path)) { return 0; } $fullPath = $this->getSourcePath($path); if (PHP_INT_SIZE === 4) { $helper = new \OC\LargeFileHelper; return $helper->getFileSize($fullPath); } return filesize($fullPath); } public function isReadable($path) { return is_readable($this->getSourcePath($path)); } public function isUpdatable($path) { return is_writable($this->getSourcePath($path)); } public function file_exists($path) { return file_exists($this->getSourcePath($path)); } public function filemtime($path) { $fullPath = $this->getSourcePath($path); clearstatcache($fullPath); if (!$this->file_exists($path)) { return false; } if (PHP_INT_SIZE === 4) { $helper = new \OC\LargeFileHelper(); return $helper->getFileMtime($fullPath); } return filemtime($fullPath); } public function touch($path, $mtime = null) { // sets the modification time of the file to the given value. // If mtime is nil the current time is set. // note that the access time of the file always changes to the current time. if ($this->file_exists($path) and !$this->isUpdatable($path)) { return false; } if (!is_null($mtime)) { $result = touch($this->getSourcePath($path), $mtime); } else { $result = touch($this->getSourcePath($path)); } if ($result) { clearstatcache(true, $this->getSourcePath($path)); } return $result; } public function file_get_contents($path) { return file_get_contents($this->getSourcePath($path)); } public function file_put_contents($path, $data) { return file_put_contents($this->getSourcePath($path), $data); } public function unlink($path) { if ($this->is_dir($path)) { return $this->rmdir($path); } else if ($this->is_file($path)) { return unlink($this->getSourcePath($path)); } else { return false; } } public function rename($path1, $path2) { $srcParent = dirname($path1); $dstParent = dirname($path2); if (!$this->isUpdatable($srcParent)) { \OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, \OCP\Util::ERROR); return false; } if (!$this->isUpdatable($dstParent)) { \OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, \OCP\Util::ERROR); return false; } if (!$this->file_exists($path1)) { \OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, \OCP\Util::ERROR); return false; } if ($this->is_dir($path2)) { $this->rmdir($path2); } else if ($this->is_file($path2)) { $this->unlink($path2); } if ($this->is_dir($path1)) { // we can't move folders across devices, use copy instead $stat1 = stat(dirname($this->getSourcePath($path1))); $stat2 = stat(dirname($this->getSourcePath($path2))); if ($stat1['dev'] !== $stat2['dev']) { $result = $this->copy($path1, $path2); if ($result) { $result &= $this->rmdir($path1); } return $result; } } return rename($this->getSourcePath($path1), $this->getSourcePath($path2)); } public function copy($path1, $path2) { if ($this->is_dir($path1)) { return parent::copy($path1, $path2); } else { return copy($this->getSourcePath($path1), $this->getSourcePath($path2)); } } public function fopen($path, $mode) { return fopen($this->getSourcePath($path), $mode); } public function hash($type, $path, $raw = false) { return hash_file($type, $this->getSourcePath($path), $raw); } public function free_space($path) { $sourcePath = $this->getSourcePath($path); // using !is_dir because $sourcePath might be a part file or // non-existing file, so we'd still want to use the parent dir // in such cases if (!is_dir($sourcePath)) { // disk_free_space doesn't work on files $sourcePath = dirname($sourcePath); } $space = @disk_free_space($sourcePath); if ($space === false || is_null($space)) { return \OCP\Files\FileInfo::SPACE_UNKNOWN; } return $space; } public function search($query) { return $this->searchInDir($query); } public function getLocalFile($path) { return $this->getSourcePath($path); } public function getLocalFolder($path) { return $this->getSourcePath($path); } /** * @param string $query * @param string $dir * @return array */ protected function searchInDir($query, $dir = '') { $files = array(); $physicalDir = $this->getSourcePath($dir); foreach (scandir($physicalDir) as $item) { if (\OC\Files\Filesystem::isIgnoredDir($item)) continue; $physicalItem = $physicalDir . '/' . $item; if (strstr(strtolower($item), strtolower($query)) !== false) { $files[] = $dir . '/' . $item; } if (is_dir($physicalItem)) { $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item)); } } return $files; } /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @return bool */ public function hasUpdated($path, $time) { if ($this->file_exists($path)) { return $this->filemtime($path) > $time; } else { return true; } } /** * Get the source path (on disk) of a given path * * @param string $path * @return string * @throws ForbiddenException */ public function getSourcePath($path) { $fullPath = $this->datadir . $path; if ($this->allowSymlinks || $path === '') { return $fullPath; } $pathToResolve = $fullPath; $realPath = realpath($pathToResolve); while ($realPath === false) { // for non existing files check the parent directory $pathToResolve = dirname($pathToResolve); $realPath = realpath($pathToResolve); } if ($realPath) { $realPath = $realPath . '/'; } if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) { return $fullPath; } \OCP\Util::writeLog('core', "Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", \OCP\Util::ERROR); throw new ForbiddenException('Following symlinks is not allowed', false); } /** * {@inheritdoc} */ public function isLocal() { return true; } /** * get the ETag for a file or folder * * @param string $path * @return string */ public function getETag($path) { if ($this->is_file($path)) { $stat = $this->stat($path); return md5( $stat['mtime'] . $stat['ino'] . $stat['dev'] . $stat['size'] ); } else { return parent::getETag($path); } } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) { if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')) { if ($sourceStorage->instanceOfStorage(Jail::class)) { /** * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage */ $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath); } /** * @var \OC\Files\Storage\Local $sourceStorage */ $rootStorage = new Local(['datadir' => '/']); return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath)); } else { return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { if ($sourceStorage->instanceOfStorage(Local::class)) { if ($sourceStorage->instanceOfStorage(Jail::class)) { /** * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage */ $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath); } /** * @var \OC\Files\Storage\Local $sourceStorage */ $rootStorage = new Local(['datadir' => '/']); return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath)); } else { return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } } } private/Files/Storage/Wrapper/Availability.php 0000604 00000025454 15247130452 0015440 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage\Wrapper; /** * Availability checker for storages * * Throws a StorageNotAvailableException for storages with known failures */ class Availability extends Wrapper { const RECHECK_TTL_SEC = 600; // 10 minutes public static function shouldRecheck($availability) { if (!$availability['available']) { // trigger a recheck if TTL reached if ((time() - $availability['last_checked']) > self::RECHECK_TTL_SEC) { return true; } } return false; } /** * Only called if availability === false * * @return bool */ private function updateAvailability() { // reset availability to false so that multiple requests don't recheck concurrently $this->setAvailability(false); try { $result = $this->test(); } catch (\Exception $e) { $result = false; } $this->setAvailability($result); return $result; } /** * @return bool */ private function isAvailable() { $availability = $this->getAvailability(); if (self::shouldRecheck($availability)) { return $this->updateAvailability(); } return $availability['available']; } /** * @throws \OCP\Files\StorageNotAvailableException */ private function checkAvailability() { if (!$this->isAvailable()) { throw new \OCP\Files\StorageNotAvailableException(); } } /** {@inheritdoc} */ public function mkdir($path) { $this->checkAvailability(); try { return parent::mkdir($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function rmdir($path) { $this->checkAvailability(); try { return parent::rmdir($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function opendir($path) { $this->checkAvailability(); try { return parent::opendir($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function is_dir($path) { $this->checkAvailability(); try { return parent::is_dir($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function is_file($path) { $this->checkAvailability(); try { return parent::is_file($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function stat($path) { $this->checkAvailability(); try { return parent::stat($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function filetype($path) { $this->checkAvailability(); try { return parent::filetype($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function filesize($path) { $this->checkAvailability(); try { return parent::filesize($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function isCreatable($path) { $this->checkAvailability(); try { return parent::isCreatable($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function isReadable($path) { $this->checkAvailability(); try { return parent::isReadable($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function isUpdatable($path) { $this->checkAvailability(); try { return parent::isUpdatable($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function isDeletable($path) { $this->checkAvailability(); try { return parent::isDeletable($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function isSharable($path) { $this->checkAvailability(); try { return parent::isSharable($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function getPermissions($path) { $this->checkAvailability(); try { return parent::getPermissions($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function file_exists($path) { if ($path === '') { return true; } $this->checkAvailability(); try { return parent::file_exists($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function filemtime($path) { $this->checkAvailability(); try { return parent::filemtime($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function file_get_contents($path) { $this->checkAvailability(); try { return parent::file_get_contents($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function file_put_contents($path, $data) { $this->checkAvailability(); try { return parent::file_put_contents($path, $data); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function unlink($path) { $this->checkAvailability(); try { return parent::unlink($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function rename($path1, $path2) { $this->checkAvailability(); try { return parent::rename($path1, $path2); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function copy($path1, $path2) { $this->checkAvailability(); try { return parent::copy($path1, $path2); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function fopen($path, $mode) { $this->checkAvailability(); try { return parent::fopen($path, $mode); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function getMimeType($path) { $this->checkAvailability(); try { return parent::getMimeType($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function hash($type, $path, $raw = false) { $this->checkAvailability(); try { return parent::hash($type, $path, $raw); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function free_space($path) { $this->checkAvailability(); try { return parent::free_space($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function search($query) { $this->checkAvailability(); try { return parent::search($query); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function touch($path, $mtime = null) { $this->checkAvailability(); try { return parent::touch($path, $mtime); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function getLocalFile($path) { $this->checkAvailability(); try { return parent::getLocalFile($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function hasUpdated($path, $time) { $this->checkAvailability(); try { return parent::hasUpdated($path, $time); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function getOwner($path) { try { return parent::getOwner($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function getETag($path) { $this->checkAvailability(); try { return parent::getETag($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function getDirectDownload($path) { $this->checkAvailability(); try { return parent::getDirectDownload($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { $this->checkAvailability(); try { return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { $this->checkAvailability(); try { return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } /** {@inheritdoc} */ public function getMetaData($path) { $this->checkAvailability(); try { return parent::getMetaData($path); } catch (\OCP\Files\StorageNotAvailableException $e) { $this->setAvailability(false); throw $e; } } } private/Files/Storage/Wrapper/Encoding.php 0000604 00000032716 15247130452 0014553 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage\Wrapper; use OCP\ICache; use OC\Cache\CappedMemoryCache; /** * Encoding wrapper that deals with file names that use unsupported encodings like NFD. * * When applied and a UTF-8 path name was given, the wrapper will first attempt to access * the actual given name and then try its NFD form. */ class Encoding extends Wrapper { /** * @var ICache */ private $namesCache; /** * @param array $parameters */ public function __construct($parameters) { $this->storage = $parameters['storage']; $this->namesCache = new CappedMemoryCache(); } /** * Returns whether the given string is only made of ASCII characters * * @param string $str string * * @return bool true if the string is all ASCII, false otherwise */ private function isAscii($str) { return (bool) !preg_match('/[\\x80-\\xff]+/', $str); } /** * Checks whether the given path exists in NFC or NFD form after checking * each form for each path section and returns the correct form. * If no existing path found, returns the path as it was given. * * @param string $fullPath path to check * * @return string original or converted path */ private function findPathToUse($fullPath) { $cachedPath = $this->namesCache[$fullPath]; if ($cachedPath !== null) { return $cachedPath; } $sections = explode('/', $fullPath); $path = ''; foreach ($sections as $section) { $convertedPath = $this->findPathToUseLastSection($path, $section); if ($convertedPath === null) { // no point in continuing if the section was not found, use original path return $fullPath; } $path = $convertedPath . '/'; } $path = rtrim($path, '/'); return $path; } /** * Checks whether the last path section of the given path exists in NFC or NFD form * and returns the correct form. If no existing path found, returns null. * * @param string $basePath base path to check * @param string $lastSection last section of the path to check for NFD/NFC variations * * @return string|null original or converted path, or null if none of the forms was found */ private function findPathToUseLastSection($basePath, $lastSection) { $fullPath = $basePath . $lastSection; if ($lastSection === '' || $this->isAscii($lastSection) || $this->storage->file_exists($fullPath)) { $this->namesCache[$fullPath] = $fullPath; return $fullPath; } // swap encoding if (\Normalizer::isNormalized($lastSection, \Normalizer::FORM_C)) { $otherFormPath = \Normalizer::normalize($lastSection, \Normalizer::FORM_D); } else { $otherFormPath = \Normalizer::normalize($lastSection, \Normalizer::FORM_C); } $otherFullPath = $basePath . $otherFormPath; if ($this->storage->file_exists($otherFullPath)) { $this->namesCache[$fullPath] = $otherFullPath; return $otherFullPath; } // return original path, file did not exist at all $this->namesCache[$fullPath] = $fullPath; return null; } /** * see http://php.net/manual/en/function.mkdir.php * * @param string $path * @return bool */ public function mkdir($path) { // note: no conversion here, method should not be called with non-NFC names! $result = $this->storage->mkdir($path); if ($result) { $this->namesCache[$path] = $path; } return $result; } /** * see http://php.net/manual/en/function.rmdir.php * * @param string $path * @return bool */ public function rmdir($path) { $result = $this->storage->rmdir($this->findPathToUse($path)); if ($result) { unset($this->namesCache[$path]); } return $result; } /** * see http://php.net/manual/en/function.opendir.php * * @param string $path * @return resource */ public function opendir($path) { return $this->storage->opendir($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.is_dir.php * * @param string $path * @return bool */ public function is_dir($path) { return $this->storage->is_dir($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.is_file.php * * @param string $path * @return bool */ public function is_file($path) { return $this->storage->is_file($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.stat.php * only the following keys are required in the result: size and mtime * * @param string $path * @return array */ public function stat($path) { return $this->storage->stat($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.filetype.php * * @param string $path * @return bool */ public function filetype($path) { return $this->storage->filetype($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.filesize.php * The result for filesize when called on a folder is required to be 0 * * @param string $path * @return int */ public function filesize($path) { return $this->storage->filesize($this->findPathToUse($path)); } /** * check if a file can be created in $path * * @param string $path * @return bool */ public function isCreatable($path) { return $this->storage->isCreatable($this->findPathToUse($path)); } /** * check if a file can be read * * @param string $path * @return bool */ public function isReadable($path) { return $this->storage->isReadable($this->findPathToUse($path)); } /** * check if a file can be written to * * @param string $path * @return bool */ public function isUpdatable($path) { return $this->storage->isUpdatable($this->findPathToUse($path)); } /** * check if a file can be deleted * * @param string $path * @return bool */ public function isDeletable($path) { return $this->storage->isDeletable($this->findPathToUse($path)); } /** * check if a file can be shared * * @param string $path * @return bool */ public function isSharable($path) { return $this->storage->isSharable($this->findPathToUse($path)); } /** * get the full permissions of a path. * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php * * @param string $path * @return int */ public function getPermissions($path) { return $this->storage->getPermissions($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.file_exists.php * * @param string $path * @return bool */ public function file_exists($path) { return $this->storage->file_exists($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.filemtime.php * * @param string $path * @return int */ public function filemtime($path) { return $this->storage->filemtime($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.file_get_contents.php * * @param string $path * @return string */ public function file_get_contents($path) { return $this->storage->file_get_contents($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.file_put_contents.php * * @param string $path * @param string $data * @return bool */ public function file_put_contents($path, $data) { return $this->storage->file_put_contents($this->findPathToUse($path), $data); } /** * see http://php.net/manual/en/function.unlink.php * * @param string $path * @return bool */ public function unlink($path) { $result = $this->storage->unlink($this->findPathToUse($path)); if ($result) { unset($this->namesCache[$path]); } return $result; } /** * see http://php.net/manual/en/function.rename.php * * @param string $path1 * @param string $path2 * @return bool */ public function rename($path1, $path2) { // second name always NFC return $this->storage->rename($this->findPathToUse($path1), $this->findPathToUse($path2)); } /** * see http://php.net/manual/en/function.copy.php * * @param string $path1 * @param string $path2 * @return bool */ public function copy($path1, $path2) { return $this->storage->copy($this->findPathToUse($path1), $this->findPathToUse($path2)); } /** * see http://php.net/manual/en/function.fopen.php * * @param string $path * @param string $mode * @return resource */ public function fopen($path, $mode) { $result = $this->storage->fopen($this->findPathToUse($path), $mode); if ($result && $mode !== 'r' && $mode !== 'rb') { unset($this->namesCache[$path]); } return $result; } /** * get the mimetype for a file or folder * The mimetype for a folder is required to be "httpd/unix-directory" * * @param string $path * @return string */ public function getMimeType($path) { return $this->storage->getMimeType($this->findPathToUse($path)); } /** * see http://php.net/manual/en/function.hash.php * * @param string $type * @param string $path * @param bool $raw * @return string */ public function hash($type, $path, $raw = false) { return $this->storage->hash($type, $this->findPathToUse($path), $raw); } /** * see http://php.net/manual/en/function.free_space.php * * @param string $path * @return int */ public function free_space($path) { return $this->storage->free_space($this->findPathToUse($path)); } /** * search for occurrences of $query in file names * * @param string $query * @return array */ public function search($query) { return $this->storage->search($query); } /** * see http://php.net/manual/en/function.touch.php * If the backend does not support the operation, false should be returned * * @param string $path * @param int $mtime * @return bool */ public function touch($path, $mtime = null) { return $this->storage->touch($this->findPathToUse($path), $mtime); } /** * get the path to a local version of the file. * The local version of the file can be temporary and doesn't have to be persistent across requests * * @param string $path * @return string */ public function getLocalFile($path) { return $this->storage->getLocalFile($this->findPathToUse($path)); } /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @return bool * * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. * returning true for other changes in the folder is optional */ public function hasUpdated($path, $time) { return $this->storage->hasUpdated($this->findPathToUse($path), $time); } /** * get a cache instance for the storage * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache * @return \OC\Files\Cache\Cache */ public function getCache($path = '', $storage = null) { if (!$storage) { $storage = $this; } return $this->storage->getCache($this->findPathToUse($path), $storage); } /** * get a scanner instance for the storage * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner * @return \OC\Files\Cache\Scanner */ public function getScanner($path = '', $storage = null) { if (!$storage) { $storage = $this; } return $this->storage->getScanner($this->findPathToUse($path), $storage); } /** * get the ETag for a file or folder * * @param string $path * @return string */ public function getETag($path) { return $this->storage->getETag($this->findPathToUse($path)); } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { if ($sourceStorage === $this) { return $this->copy($sourceInternalPath, $this->findPathToUse($targetInternalPath)); } $result = $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $this->findPathToUse($targetInternalPath)); if ($result) { unset($this->namesCache[$targetInternalPath]); } return $result; } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { if ($sourceStorage === $this) { $result = $this->rename($sourceInternalPath, $this->findPathToUse($targetInternalPath)); if ($result) { unset($this->namesCache[$sourceInternalPath]); unset($this->namesCache[$targetInternalPath]); } return $result; } $result = $this->storage->moveFromStorage($sourceStorage, $sourceInternalPath, $this->findPathToUse($targetInternalPath)); if ($result) { unset($this->namesCache[$sourceInternalPath]); unset($this->namesCache[$targetInternalPath]); } return $result; } /** * @param string $path * @return array */ public function getMetaData($path) { return $this->storage->getMetaData($this->findPathToUse($path)); } } private/Files/Storage/Wrapper/Encryption.php 0000604 00000073453 15247130452 0015162 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage\Wrapper; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; use OC\Encryption\Update; use OC\Encryption\Util; use OC\Files\Cache\CacheEntry; use OC\Files\Filesystem; use OC\Files\Mount\Manager; use OC\Files\Storage\LocalTempFileTrait; use OC\Memcache\ArrayCache; use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\Encryption\IFile; use OCP\Encryption\IManager; use OCP\Encryption\Keys\IStorage; use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage; use OCP\ILogger; use OCP\Files\Cache\ICacheEntry; class Encryption extends Wrapper { use LocalTempFileTrait; /** @var string */ private $mountPoint; /** @var \OC\Encryption\Util */ private $util; /** @var \OCP\Encryption\IManager */ private $encryptionManager; /** @var \OCP\ILogger */ private $logger; /** @var string */ private $uid; /** @var array */ protected $unencryptedSize; /** @var \OCP\Encryption\IFile */ private $fileHelper; /** @var IMountPoint */ private $mount; /** @var IStorage */ private $keyStorage; /** @var Update */ private $update; /** @var Manager */ private $mountManager; /** @var array remember for which path we execute the repair step to avoid recursions */ private $fixUnencryptedSizeOf = array(); /** @var ArrayCache */ private $arrayCache; /** * @param array $parameters * @param IManager $encryptionManager * @param Util $util * @param ILogger $logger * @param IFile $fileHelper * @param string $uid * @param IStorage $keyStorage * @param Update $update * @param Manager $mountManager * @param ArrayCache $arrayCache */ public function __construct( $parameters, IManager $encryptionManager = null, Util $util = null, ILogger $logger = null, IFile $fileHelper = null, $uid = null, IStorage $keyStorage = null, Update $update = null, Manager $mountManager = null, ArrayCache $arrayCache = null ) { $this->mountPoint = $parameters['mountPoint']; $this->mount = $parameters['mount']; $this->encryptionManager = $encryptionManager; $this->util = $util; $this->logger = $logger; $this->uid = $uid; $this->fileHelper = $fileHelper; $this->keyStorage = $keyStorage; $this->unencryptedSize = array(); $this->update = $update; $this->mountManager = $mountManager; $this->arrayCache = $arrayCache; parent::__construct($parameters); } /** * see http://php.net/manual/en/function.filesize.php * The result for filesize when called on a folder is required to be 0 * * @param string $path * @return int */ public function filesize($path) { $fullPath = $this->getFullPath($path); /** @var CacheEntry $info */ $info = $this->getCache()->get($path); if (isset($this->unencryptedSize[$fullPath])) { $size = $this->unencryptedSize[$fullPath]; // update file cache if ($info instanceof ICacheEntry) { $info = $info->getData(); $info['encrypted'] = $info['encryptedVersion']; } else { if (!is_array($info)) { $info = []; } $info['encrypted'] = true; } $info['size'] = $size; $this->getCache()->put($path, $info); return $size; } if (isset($info['fileid']) && $info['encrypted']) { return $this->verifyUnencryptedSize($path, $info['size']); } return $this->storage->filesize($path); } /** * @param string $path * @return array */ public function getMetaData($path) { $data = $this->storage->getMetaData($path); if (is_null($data)) { return null; } $fullPath = $this->getFullPath($path); $info = $this->getCache()->get($path); if (isset($this->unencryptedSize[$fullPath])) { $data['encrypted'] = true; $data['size'] = $this->unencryptedSize[$fullPath]; } else { if (isset($info['fileid']) && $info['encrypted']) { $data['size'] = $this->verifyUnencryptedSize($path, $info['size']); $data['encrypted'] = true; } } if (isset($info['encryptedVersion']) && $info['encryptedVersion'] > 1) { $data['encryptedVersion'] = $info['encryptedVersion']; } return $data; } /** * see http://php.net/manual/en/function.file_get_contents.php * * @param string $path * @return string */ public function file_get_contents($path) { $encryptionModule = $this->getEncryptionModule($path); if ($encryptionModule) { $handle = $this->fopen($path, "r"); if (!$handle) { return false; } $data = stream_get_contents($handle); fclose($handle); return $data; } return $this->storage->file_get_contents($path); } /** * see http://php.net/manual/en/function.file_put_contents.php * * @param string $path * @param string $data * @return bool */ public function file_put_contents($path, $data) { // file put content will always be translated to a stream write $handle = $this->fopen($path, 'w'); if (is_resource($handle)) { $written = fwrite($handle, $data); fclose($handle); return $written; } return false; } /** * see http://php.net/manual/en/function.unlink.php * * @param string $path * @return bool */ public function unlink($path) { $fullPath = $this->getFullPath($path); if ($this->util->isExcluded($fullPath)) { return $this->storage->unlink($path); } $encryptionModule = $this->getEncryptionModule($path); if ($encryptionModule) { $this->keyStorage->deleteAllFileKeys($this->getFullPath($path)); } return $this->storage->unlink($path); } /** * see http://php.net/manual/en/function.rename.php * * @param string $path1 * @param string $path2 * @return bool */ public function rename($path1, $path2) { $result = $this->storage->rename($path1, $path2); if ($result && // versions always use the keys from the original file, so we can skip // this step for versions $this->isVersion($path2) === false && $this->encryptionManager->isEnabled()) { $source = $this->getFullPath($path1); if (!$this->util->isExcluded($source)) { $target = $this->getFullPath($path2); if (isset($this->unencryptedSize[$source])) { $this->unencryptedSize[$target] = $this->unencryptedSize[$source]; } $this->keyStorage->renameKeys($source, $target); $module = $this->getEncryptionModule($path2); if ($module) { $module->update($target, $this->uid, []); } } } return $result; } /** * see http://php.net/manual/en/function.rmdir.php * * @param string $path * @return bool */ public function rmdir($path) { $result = $this->storage->rmdir($path); $fullPath = $this->getFullPath($path); if ($result && $this->util->isExcluded($fullPath) === false && $this->encryptionManager->isEnabled() ) { $this->keyStorage->deleteAllFileKeys($fullPath); } return $result; } /** * check if a file can be read * * @param string $path * @return bool */ public function isReadable($path) { $isReadable = true; $metaData = $this->getMetaData($path); if ( !$this->is_dir($path) && isset($metaData['encrypted']) && $metaData['encrypted'] === true ) { $fullPath = $this->getFullPath($path); $module = $this->getEncryptionModule($path); $isReadable = $module->isReadable($fullPath, $this->uid); } return $this->storage->isReadable($path) && $isReadable; } /** * see http://php.net/manual/en/function.copy.php * * @param string $path1 * @param string $path2 * @return bool */ public function copy($path1, $path2) { $source = $this->getFullPath($path1); if ($this->util->isExcluded($source)) { return $this->storage->copy($path1, $path2); } // need to stream copy file by file in case we copy between a encrypted // and a unencrypted storage $this->unlink($path2); $result = $this->copyFromStorage($this, $path1, $path2); return $result; } /** * see http://php.net/manual/en/function.fopen.php * * @param string $path * @param string $mode * @return resource|bool * @throws GenericEncryptionException * @throws ModuleDoesNotExistsException */ public function fopen($path, $mode) { // check if the file is stored in the array cache, this means that we // copy a file over to the versions folder, in this case we don't want to // decrypt it if ($this->arrayCache->hasKey('encryption_copy_version_' . $path)) { $this->arrayCache->remove('encryption_copy_version_' . $path); return $this->storage->fopen($path, $mode); } $encryptionEnabled = $this->encryptionManager->isEnabled(); $shouldEncrypt = false; $encryptionModule = null; $header = $this->getHeader($path); $signed = (isset($header['signed']) && $header['signed'] === 'true') ? true : false; $fullPath = $this->getFullPath($path); $encryptionModuleId = $this->util->getEncryptionModuleId($header); if ($this->util->isExcluded($fullPath) === false) { $size = $unencryptedSize = 0; $realFile = $this->util->stripPartialFileExtension($path); $targetExists = $this->file_exists($realFile) || $this->file_exists($path); $targetIsEncrypted = false; if ($targetExists) { // in case the file exists we require the explicit module as // specified in the file header - otherwise we need to fail hard to // prevent data loss on client side if (!empty($encryptionModuleId)) { $targetIsEncrypted = true; $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); } if ($this->file_exists($path)) { $size = $this->storage->filesize($path); $unencryptedSize = $this->filesize($path); } else { $size = $unencryptedSize = 0; } } try { if ( $mode === 'w' || $mode === 'w+' || $mode === 'wb' || $mode === 'wb+' ) { // don't overwrite encrypted files if encryption is not enabled if ($targetIsEncrypted && $encryptionEnabled === false) { throw new GenericEncryptionException('Tried to access encrypted file but encryption is not enabled'); } if ($encryptionEnabled) { // if $encryptionModuleId is empty, the default module will be used $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); $shouldEncrypt = $encryptionModule->shouldEncrypt($fullPath); $signed = true; } } else { $info = $this->getCache()->get($path); // only get encryption module if we found one in the header // or if file should be encrypted according to the file cache if (!empty($encryptionModuleId)) { $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); $shouldEncrypt = true; } else if (empty($encryptionModuleId) && $info['encrypted'] === true) { // we come from a old installation. No header and/or no module defined // but the file is encrypted. In this case we need to use the // OC_DEFAULT_MODULE to read the file $encryptionModule = $this->encryptionManager->getEncryptionModule('OC_DEFAULT_MODULE'); $shouldEncrypt = true; $targetIsEncrypted = true; } } } catch (ModuleDoesNotExistsException $e) { $this->logger->warning('Encryption module "' . $encryptionModuleId . '" not found, file will be stored unencrypted (' . $e->getMessage() . ')'); } // encryption disabled on write of new file and write to existing unencrypted file -> don't encrypt if (!$encryptionEnabled || !$this->shouldEncrypt($path)) { if (!$targetExists || !$targetIsEncrypted) { $shouldEncrypt = false; } } if ($shouldEncrypt === true && $encryptionModule !== null) { $headerSize = $this->getHeaderSize($path); $source = $this->storage->fopen($path, $mode); if (!is_resource($source)) { return false; } $handle = \OC\Files\Stream\Encryption::wrap($source, $path, $fullPath, $header, $this->uid, $encryptionModule, $this->storage, $this, $this->util, $this->fileHelper, $mode, $size, $unencryptedSize, $headerSize, $signed); return $handle; } } return $this->storage->fopen($path, $mode); } /** * perform some plausibility checks if the the unencrypted size is correct. * If not, we calculate the correct unencrypted size and return it * * @param string $path internal path relative to the storage root * @param int $unencryptedSize size of the unencrypted file * * @return int unencrypted size */ protected function verifyUnencryptedSize($path, $unencryptedSize) { $size = $this->storage->filesize($path); $result = $unencryptedSize; if ($unencryptedSize < 0 || ($size > 0 && $unencryptedSize === $size) ) { // check if we already calculate the unencrypted size for the // given path to avoid recursions if (isset($this->fixUnencryptedSizeOf[$this->getFullPath($path)]) === false) { $this->fixUnencryptedSizeOf[$this->getFullPath($path)] = true; try { $result = $this->fixUnencryptedSize($path, $size, $unencryptedSize); } catch (\Exception $e) { $this->logger->error('Couldn\'t re-calculate unencrypted size for '. $path); $this->logger->logException($e); } unset($this->fixUnencryptedSizeOf[$this->getFullPath($path)]); } } return $result; } /** * calculate the unencrypted size * * @param string $path internal path relative to the storage root * @param int $size size of the physical file * @param int $unencryptedSize size of the unencrypted file * * @return int calculated unencrypted size */ protected function fixUnencryptedSize($path, $size, $unencryptedSize) { $headerSize = $this->getHeaderSize($path); $header = $this->getHeader($path); $encryptionModule = $this->getEncryptionModule($path); $stream = $this->storage->fopen($path, 'r'); // if we couldn't open the file we return the old unencrypted size if (!is_resource($stream)) { $this->logger->error('Could not open ' . $path . '. Recalculation of unencrypted size aborted.'); return $unencryptedSize; } $newUnencryptedSize = 0; $size -= $headerSize; $blockSize = $this->util->getBlockSize(); // if a header exists we skip it if ($headerSize > 0) { fread($stream, $headerSize); } // fast path, else the calculation for $lastChunkNr is bogus if ($size === 0) { return 0; } $signed = (isset($header['signed']) && $header['signed'] === 'true') ? true : false; $unencryptedBlockSize = $encryptionModule->getUnencryptedBlockSize($signed); // calculate last chunk nr // next highest is end of chunks, one subtracted is last one // we have to read the last chunk, we can't just calculate it (because of padding etc) $lastChunkNr = ceil($size/ $blockSize)-1; // calculate last chunk position $lastChunkPos = ($lastChunkNr * $blockSize); // try to fseek to the last chunk, if it fails we have to read the whole file if (@fseek($stream, $lastChunkPos, SEEK_CUR) === 0) { $newUnencryptedSize += $lastChunkNr * $unencryptedBlockSize; } $lastChunkContentEncrypted=''; $count = $blockSize; while ($count > 0) { $data=fread($stream, $blockSize); $count=strlen($data); $lastChunkContentEncrypted .= $data; if(strlen($lastChunkContentEncrypted) > $blockSize) { $newUnencryptedSize += $unencryptedBlockSize; $lastChunkContentEncrypted=substr($lastChunkContentEncrypted, $blockSize); } } fclose($stream); // we have to decrypt the last chunk to get it actual size $encryptionModule->begin($this->getFullPath($path), $this->uid, 'r', $header, []); $decryptedLastChunk = $encryptionModule->decrypt($lastChunkContentEncrypted, $lastChunkNr . 'end'); $decryptedLastChunk .= $encryptionModule->end($this->getFullPath($path), $lastChunkNr . 'end'); // calc the real file size with the size of the last chunk $newUnencryptedSize += strlen($decryptedLastChunk); $this->updateUnencryptedSize($this->getFullPath($path), $newUnencryptedSize); // write to cache if applicable $cache = $this->storage->getCache(); if ($cache) { $entry = $cache->get($path); $cache->update($entry['fileid'], ['size' => $newUnencryptedSize]); } return $newUnencryptedSize; } /** * @param Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @param bool $preserveMtime * @return bool */ public function moveFromStorage(Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = true) { if ($sourceStorage === $this) { return $this->rename($sourceInternalPath, $targetInternalPath); } // TODO clean this up once the underlying moveFromStorage in OC\Files\Storage\Wrapper\Common is fixed: // - call $this->storage->moveFromStorage() instead of $this->copyBetweenStorage // - copy the file cache update from $this->copyBetweenStorage to this method // - copy the copyKeys() call from $this->copyBetweenStorage to this method // - remove $this->copyBetweenStorage if (!$sourceStorage->isDeletable($sourceInternalPath)) { return false; } $result = $this->copyBetweenStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, true); if ($result) { if ($sourceStorage->is_dir($sourceInternalPath)) { $result &= $sourceStorage->rmdir($sourceInternalPath); } else { $result &= $sourceStorage->unlink($sourceInternalPath); } } return $result; } /** * @param Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @param bool $preserveMtime * @param bool $isRename * @return bool */ public function copyFromStorage(Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false, $isRename = false) { // TODO clean this up once the underlying moveFromStorage in OC\Files\Storage\Wrapper\Common is fixed: // - call $this->storage->copyFromStorage() instead of $this->copyBetweenStorage // - copy the file cache update from $this->copyBetweenStorage to this method // - copy the copyKeys() call from $this->copyBetweenStorage to this method // - remove $this->copyBetweenStorage return $this->copyBetweenStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename); } /** * Update the encrypted cache version in the database * * @param Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @param bool $isRename */ private function updateEncryptedVersion(Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename) { $isEncrypted = $this->encryptionManager->isEnabled() && $this->shouldEncrypt($targetInternalPath) ? 1 : 0; $cacheInformation = [ 'encrypted' => (bool)$isEncrypted, ]; if($isEncrypted === 1) { $encryptedVersion = $sourceStorage->getCache()->get($sourceInternalPath)['encryptedVersion']; // In case of a move operation from an unencrypted to an encrypted // storage the old encrypted version would stay with "0" while the // correct value would be "1". Thus we manually set the value to "1" // for those cases. // See also https://github.com/owncloud/core/issues/23078 if($encryptedVersion === 0) { $encryptedVersion = 1; } $cacheInformation['encryptedVersion'] = $encryptedVersion; } // in case of a rename we need to manipulate the source cache because // this information will be kept for the new target if ($isRename) { $sourceStorage->getCache()->put($sourceInternalPath, $cacheInformation); } else { $this->getCache()->put($targetInternalPath, $cacheInformation); } } /** * copy file between two storages * * @param Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @param bool $preserveMtime * @param bool $isRename * @return bool * @throws \Exception */ private function copyBetweenStorage(Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename) { // for versions we have nothing to do, because versions should always use the // key from the original file. Just create a 1:1 copy and done if ($this->isVersion($targetInternalPath) || $this->isVersion($sourceInternalPath)) { // remember that we try to create a version so that we can detect it during // fopen($sourceInternalPath) and by-pass the encryption in order to // create a 1:1 copy of the file $this->arrayCache->set('encryption_copy_version_' . $sourceInternalPath, true); $result = $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); $this->arrayCache->remove('encryption_copy_version_' . $sourceInternalPath); if ($result) { $info = $this->getCache('', $sourceStorage)->get($sourceInternalPath); // make sure that we update the unencrypted size for the version if (isset($info['encrypted']) && $info['encrypted'] === true) { $this->updateUnencryptedSize( $this->getFullPath($targetInternalPath), $info['size'] ); } $this->updateEncryptedVersion($sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename); } return $result; } // first copy the keys that we reuse the existing file key on the target location // and don't create a new one which would break versions for example. $mount = $this->mountManager->findByStorageId($sourceStorage->getId()); if (count($mount) === 1) { $mountPoint = $mount[0]->getMountPoint(); $source = $mountPoint . '/' . $sourceInternalPath; $target = $this->getFullPath($targetInternalPath); $this->copyKeys($source, $target); } else { $this->logger->error('Could not find mount point, can\'t keep encryption keys'); } if ($sourceStorage->is_dir($sourceInternalPath)) { $dh = $sourceStorage->opendir($sourceInternalPath); $result = $this->mkdir($targetInternalPath); if (is_resource($dh)) { while ($result and ($file = readdir($dh)) !== false) { if (!Filesystem::isIgnoredDir($file)) { $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file, false, $isRename); } } } } else { try { $source = $sourceStorage->fopen($sourceInternalPath, 'r'); $target = $this->fopen($targetInternalPath, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); fclose($source); fclose($target); } catch (\Exception $e) { fclose($source); fclose($target); throw $e; } if($result) { if ($preserveMtime) { $this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath)); } $this->updateEncryptedVersion($sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename); } else { // delete partially written target file $this->unlink($targetInternalPath); // delete cache entry that was created by fopen $this->getCache()->remove($targetInternalPath); } } return (bool)$result; } /** * get the path to a local version of the file. * The local version of the file can be temporary and doesn't have to be persistent across requests * * @param string $path * @return string */ public function getLocalFile($path) { if ($this->encryptionManager->isEnabled()) { $cachedFile = $this->getCachedFile($path); if (is_string($cachedFile)) { return $cachedFile; } } return $this->storage->getLocalFile($path); } /** * Returns the wrapped storage's value for isLocal() * * @return bool wrapped storage's isLocal() value */ public function isLocal() { if ($this->encryptionManager->isEnabled()) { return false; } return $this->storage->isLocal(); } /** * see http://php.net/manual/en/function.stat.php * only the following keys are required in the result: size and mtime * * @param string $path * @return array */ public function stat($path) { $stat = $this->storage->stat($path); $fileSize = $this->filesize($path); $stat['size'] = $fileSize; $stat[7] = $fileSize; return $stat; } /** * see http://php.net/manual/en/function.hash.php * * @param string $type * @param string $path * @param bool $raw * @return string */ public function hash($type, $path, $raw = false) { $fh = $this->fopen($path, 'rb'); $ctx = hash_init($type); hash_update_stream($ctx, $fh); fclose($fh); return hash_final($ctx, $raw); } /** * return full path, including mount point * * @param string $path relative to mount point * @return string full path including mount point */ protected function getFullPath($path) { return Filesystem::normalizePath($this->mountPoint . '/' . $path); } /** * read first block of encrypted file, typically this will contain the * encryption header * * @param string $path * @return string */ protected function readFirstBlock($path) { $firstBlock = ''; if ($this->storage->file_exists($path)) { $handle = $this->storage->fopen($path, 'r'); $firstBlock = fread($handle, $this->util->getHeaderSize()); fclose($handle); } return $firstBlock; } /** * return header size of given file * * @param string $path * @return int */ protected function getHeaderSize($path) { $headerSize = 0; $realFile = $this->util->stripPartialFileExtension($path); if ($this->storage->file_exists($realFile)) { $path = $realFile; } $firstBlock = $this->readFirstBlock($path); if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) { $headerSize = $this->util->getHeaderSize(); } return $headerSize; } /** * parse raw header to array * * @param string $rawHeader * @return array */ protected function parseRawHeader($rawHeader) { $result = array(); if (substr($rawHeader, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) { $header = $rawHeader; $endAt = strpos($header, Util::HEADER_END); if ($endAt !== false) { $header = substr($header, 0, $endAt + strlen(Util::HEADER_END)); // +1 to not start with an ':' which would result in empty element at the beginning $exploded = explode(':', substr($header, strlen(Util::HEADER_START)+1)); $element = array_shift($exploded); while ($element !== Util::HEADER_END) { $result[$element] = array_shift($exploded); $element = array_shift($exploded); } } } return $result; } /** * read header from file * * @param string $path * @return array */ protected function getHeader($path) { $realFile = $this->util->stripPartialFileExtension($path); $exists = $this->storage->file_exists($realFile); if ($exists) { $path = $realFile; } $firstBlock = $this->readFirstBlock($path); $result = $this->parseRawHeader($firstBlock); // if the header doesn't contain a encryption module we check if it is a // legacy file. If true, we add the default encryption module if (!isset($result[Util::HEADER_ENCRYPTION_MODULE_KEY])) { if (!empty($result)) { $result[Util::HEADER_ENCRYPTION_MODULE_KEY] = 'OC_DEFAULT_MODULE'; } else if ($exists) { // if the header was empty we have to check first if it is a encrypted file at all // We would do query to filecache only if we know that entry in filecache exists $info = $this->getCache()->get($path); if (isset($info['encrypted']) && $info['encrypted'] === true) { $result[Util::HEADER_ENCRYPTION_MODULE_KEY] = 'OC_DEFAULT_MODULE'; } } } return $result; } /** * read encryption module needed to read/write the file located at $path * * @param string $path * @return null|\OCP\Encryption\IEncryptionModule * @throws ModuleDoesNotExistsException * @throws \Exception */ protected function getEncryptionModule($path) { $encryptionModule = null; $header = $this->getHeader($path); $encryptionModuleId = $this->util->getEncryptionModuleId($header); if (!empty($encryptionModuleId)) { try { $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); } catch (ModuleDoesNotExistsException $e) { $this->logger->critical('Encryption module defined in "' . $path . '" not loaded!'); throw $e; } } return $encryptionModule; } /** * @param string $path * @param int $unencryptedSize */ public function updateUnencryptedSize($path, $unencryptedSize) { $this->unencryptedSize[$path] = $unencryptedSize; } /** * copy keys to new location * * @param string $source path relative to data/ * @param string $target path relative to data/ * @return bool */ protected function copyKeys($source, $target) { if (!$this->util->isExcluded($source)) { return $this->keyStorage->copyKeys($source, $target); } return false; } /** * check if path points to a files version * * @param $path * @return bool */ protected function isVersion($path) { $normalized = Filesystem::normalizePath($path); return substr($normalized, 0, strlen('/files_versions/')) === '/files_versions/'; } /** * check if the given storage should be encrypted or not * * @param $path * @return bool */ protected function shouldEncrypt($path) { $fullPath = $this->getFullPath($path); $mountPointConfig = $this->mount->getOption('encrypt', true); if ($mountPointConfig === false) { return false; } try { $encryptionModule = $this->getEncryptionModule($fullPath); } catch (ModuleDoesNotExistsException $e) { return false; } if ($encryptionModule === null) { $encryptionModule = $this->encryptionManager->getEncryptionModule(); } return $encryptionModule->shouldEncrypt($fullPath); } } private/Files/Storage/Wrapper/Wrapper.php 0000604 00000036011 15247130452 0014435 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage\Wrapper; use OCP\Files\InvalidPathException; use OCP\Files\Storage\ILockingStorage; use OCP\Lock\ILockingProvider; class Wrapper implements \OC\Files\Storage\Storage, ILockingStorage { /** * @var \OC\Files\Storage\Storage $storage */ protected $storage; public $cache; public $scanner; public $watcher; public $propagator; public $updater; /** * @param array $parameters */ public function __construct($parameters) { $this->storage = $parameters['storage']; } /** * @return \OC\Files\Storage\Storage */ public function getWrapperStorage() { return $this->storage; } /** * Get the identifier for the storage, * the returned id should be the same for every storage object that is created with the same parameters * and two storage objects with the same id should refer to two storages that display the same files. * * @return string */ public function getId() { return $this->getWrapperStorage()->getId(); } /** * see http://php.net/manual/en/function.mkdir.php * * @param string $path * @return bool */ public function mkdir($path) { return $this->getWrapperStorage()->mkdir($path); } /** * see http://php.net/manual/en/function.rmdir.php * * @param string $path * @return bool */ public function rmdir($path) { return $this->getWrapperStorage()->rmdir($path); } /** * see http://php.net/manual/en/function.opendir.php * * @param string $path * @return resource */ public function opendir($path) { return $this->getWrapperStorage()->opendir($path); } /** * see http://php.net/manual/en/function.is_dir.php * * @param string $path * @return bool */ public function is_dir($path) { return $this->getWrapperStorage()->is_dir($path); } /** * see http://php.net/manual/en/function.is_file.php * * @param string $path * @return bool */ public function is_file($path) { return $this->getWrapperStorage()->is_file($path); } /** * see http://php.net/manual/en/function.stat.php * only the following keys are required in the result: size and mtime * * @param string $path * @return array */ public function stat($path) { return $this->getWrapperStorage()->stat($path); } /** * see http://php.net/manual/en/function.filetype.php * * @param string $path * @return bool */ public function filetype($path) { return $this->getWrapperStorage()->filetype($path); } /** * see http://php.net/manual/en/function.filesize.php * The result for filesize when called on a folder is required to be 0 * * @param string $path * @return int */ public function filesize($path) { return $this->getWrapperStorage()->filesize($path); } /** * check if a file can be created in $path * * @param string $path * @return bool */ public function isCreatable($path) { return $this->getWrapperStorage()->isCreatable($path); } /** * check if a file can be read * * @param string $path * @return bool */ public function isReadable($path) { return $this->getWrapperStorage()->isReadable($path); } /** * check if a file can be written to * * @param string $path * @return bool */ public function isUpdatable($path) { return $this->getWrapperStorage()->isUpdatable($path); } /** * check if a file can be deleted * * @param string $path * @return bool */ public function isDeletable($path) { return $this->getWrapperStorage()->isDeletable($path); } /** * check if a file can be shared * * @param string $path * @return bool */ public function isSharable($path) { return $this->getWrapperStorage()->isSharable($path); } /** * get the full permissions of a path. * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php * * @param string $path * @return int */ public function getPermissions($path) { return $this->getWrapperStorage()->getPermissions($path); } /** * see http://php.net/manual/en/function.file_exists.php * * @param string $path * @return bool */ public function file_exists($path) { return $this->getWrapperStorage()->file_exists($path); } /** * see http://php.net/manual/en/function.filemtime.php * * @param string $path * @return int */ public function filemtime($path) { return $this->getWrapperStorage()->filemtime($path); } /** * see http://php.net/manual/en/function.file_get_contents.php * * @param string $path * @return string */ public function file_get_contents($path) { return $this->getWrapperStorage()->file_get_contents($path); } /** * see http://php.net/manual/en/function.file_put_contents.php * * @param string $path * @param string $data * @return bool */ public function file_put_contents($path, $data) { return $this->getWrapperStorage()->file_put_contents($path, $data); } /** * see http://php.net/manual/en/function.unlink.php * * @param string $path * @return bool */ public function unlink($path) { return $this->getWrapperStorage()->unlink($path); } /** * see http://php.net/manual/en/function.rename.php * * @param string $path1 * @param string $path2 * @return bool */ public function rename($path1, $path2) { return $this->getWrapperStorage()->rename($path1, $path2); } /** * see http://php.net/manual/en/function.copy.php * * @param string $path1 * @param string $path2 * @return bool */ public function copy($path1, $path2) { return $this->getWrapperStorage()->copy($path1, $path2); } /** * see http://php.net/manual/en/function.fopen.php * * @param string $path * @param string $mode * @return resource */ public function fopen($path, $mode) { return $this->getWrapperStorage()->fopen($path, $mode); } /** * get the mimetype for a file or folder * The mimetype for a folder is required to be "httpd/unix-directory" * * @param string $path * @return string */ public function getMimeType($path) { return $this->getWrapperStorage()->getMimeType($path); } /** * see http://php.net/manual/en/function.hash.php * * @param string $type * @param string $path * @param bool $raw * @return string */ public function hash($type, $path, $raw = false) { return $this->getWrapperStorage()->hash($type, $path, $raw); } /** * see http://php.net/manual/en/function.free_space.php * * @param string $path * @return int */ public function free_space($path) { return $this->getWrapperStorage()->free_space($path); } /** * search for occurrences of $query in file names * * @param string $query * @return array */ public function search($query) { return $this->getWrapperStorage()->search($query); } /** * see http://php.net/manual/en/function.touch.php * If the backend does not support the operation, false should be returned * * @param string $path * @param int $mtime * @return bool */ public function touch($path, $mtime = null) { return $this->getWrapperStorage()->touch($path, $mtime); } /** * get the path to a local version of the file. * The local version of the file can be temporary and doesn't have to be persistent across requests * * @param string $path * @return string */ public function getLocalFile($path) { return $this->getWrapperStorage()->getLocalFile($path); } /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @return bool * * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. * returning true for other changes in the folder is optional */ public function hasUpdated($path, $time) { return $this->getWrapperStorage()->hasUpdated($path, $time); } /** * get a cache instance for the storage * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache * @return \OC\Files\Cache\Cache */ public function getCache($path = '', $storage = null) { if (!$storage) { $storage = $this; } return $this->getWrapperStorage()->getCache($path, $storage); } /** * get a scanner instance for the storage * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner * @return \OC\Files\Cache\Scanner */ public function getScanner($path = '', $storage = null) { if (!$storage) { $storage = $this; } return $this->getWrapperStorage()->getScanner($path, $storage); } /** * get the user id of the owner of a file or folder * * @param string $path * @return string */ public function getOwner($path) { return $this->getWrapperStorage()->getOwner($path); } /** * get a watcher instance for the cache * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Watcher */ public function getWatcher($path = '', $storage = null) { if (!$storage) { $storage = $this; } return $this->getWrapperStorage()->getWatcher($path, $storage); } public function getPropagator($storage = null) { if (!$storage) { $storage = $this; } return $this->getWrapperStorage()->getPropagator($storage); } public function getUpdater($storage = null) { if (!$storage) { $storage = $this; } return $this->getWrapperStorage()->getUpdater($storage); } /** * @return \OC\Files\Cache\Storage */ public function getStorageCache() { return $this->getWrapperStorage()->getStorageCache(); } /** * get the ETag for a file or folder * * @param string $path * @return string */ public function getETag($path) { return $this->getWrapperStorage()->getETag($path); } /** * Returns true * * @return true */ public function test() { return $this->getWrapperStorage()->test(); } /** * Returns the wrapped storage's value for isLocal() * * @return bool wrapped storage's isLocal() value */ public function isLocal() { return $this->getWrapperStorage()->isLocal(); } /** * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class * * @param string $class * @return bool */ public function instanceOfStorage($class) { if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') { // FIXME Temporary fix to keep existing checks working $class = '\OCA\Files_Sharing\SharedStorage'; } return is_a($this, $class) or $this->getWrapperStorage()->instanceOfStorage($class); } /** * Pass any methods custom to specific storage implementations to the wrapped storage * * @param string $method * @param array $args * @return mixed */ public function __call($method, $args) { return call_user_func_array(array($this->getWrapperStorage(), $method), $args); } /** * A custom storage implementation can return an url for direct download of a give file. * * For now the returned array can hold the parameter url - in future more attributes might follow. * * @param string $path * @return array */ public function getDirectDownload($path) { return $this->getWrapperStorage()->getDirectDownload($path); } /** * Get availability of the storage * * @return array [ available, last_checked ] */ public function getAvailability() { return $this->getWrapperStorage()->getAvailability(); } /** * Set availability of the storage * * @param bool $isAvailable */ public function setAvailability($isAvailable) { $this->getWrapperStorage()->setAvailability($isAvailable); } /** * @param string $path the path of the target folder * @param string $fileName the name of the file itself * @return void * @throws InvalidPathException */ public function verifyPath($path, $fileName) { $this->getWrapperStorage()->verifyPath($path, $fileName); } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { if ($sourceStorage === $this) { return $this->copy($sourceInternalPath, $targetInternalPath); } return $this->getWrapperStorage()->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { if ($sourceStorage === $this) { return $this->rename($sourceInternalPath, $targetInternalPath); } return $this->getWrapperStorage()->moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } /** * @param string $path * @return array */ public function getMetaData($path) { return $this->getWrapperStorage()->getMetaData($path); } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException */ public function acquireLock($path, $type, ILockingProvider $provider) { if ($this->getWrapperStorage()->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $this->getWrapperStorage()->acquireLock($path, $type, $provider); } } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider */ public function releaseLock($path, $type, ILockingProvider $provider) { if ($this->getWrapperStorage()->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $this->getWrapperStorage()->releaseLock($path, $type, $provider); } } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider */ public function changeLock($path, $type, ILockingProvider $provider) { if ($this->getWrapperStorage()->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $this->getWrapperStorage()->changeLock($path, $type, $provider); } } /** * @return bool */ public function needsPartFile() { return $this->getWrapperStorage()->needsPartFile(); } } private/Files/Storage/Wrapper/PermissionsMask.php 0000604 00000011735 15247130452 0016152 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage\Wrapper; use OC\Files\Cache\Wrapper\CachePermissionsMask; use OCP\Constants; /** * Mask the permissions of a storage * * This can be used to restrict update, create, delete and/or share permissions of a storage * * Note that the read permissions can't be masked */ class PermissionsMask extends Wrapper { /** * @var int the permissions bits we want to keep */ private $mask; /** * @param array $arguments ['storage' => $storage, 'mask' => $mask] * * $storage: The storage the permissions mask should be applied on * $mask: The permission bits that should be kept, a combination of the \OCP\Constant::PERMISSION_ constants */ public function __construct($arguments) { parent::__construct($arguments); $this->mask = $arguments['mask']; } private function checkMask($permissions) { return ($this->mask & $permissions) === $permissions; } public function isUpdatable($path) { return $this->checkMask(Constants::PERMISSION_UPDATE) and parent::isUpdatable($path); } public function isCreatable($path) { return $this->checkMask(Constants::PERMISSION_CREATE) and parent::isCreatable($path); } public function isDeletable($path) { return $this->checkMask(Constants::PERMISSION_DELETE) and parent::isDeletable($path); } public function isSharable($path) { return $this->checkMask(Constants::PERMISSION_SHARE) and parent::isSharable($path); } public function getPermissions($path) { return $this->storage->getPermissions($path) & $this->mask; } public function rename($path1, $path2) { $p = strpos($path1, $path2); if ($p === 0) { $part = substr($path1, strlen($path2)); //This is a rename of the transfer file to the original file if (strpos($part, '.ocTransferId') === 0) { return $this->checkMask(Constants::PERMISSION_CREATE) and parent::rename($path1, $path2); } } return $this->checkMask(Constants::PERMISSION_UPDATE) and parent::rename($path1, $path2); } public function copy($path1, $path2) { return $this->checkMask(Constants::PERMISSION_CREATE) and parent::copy($path1, $path2); } public function touch($path, $mtime = null) { $permissions = $this->file_exists($path) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE; return $this->checkMask($permissions) and parent::touch($path, $mtime); } public function mkdir($path) { return $this->checkMask(Constants::PERMISSION_CREATE) and parent::mkdir($path); } public function rmdir($path) { return $this->checkMask(Constants::PERMISSION_DELETE) and parent::rmdir($path); } public function unlink($path) { return $this->checkMask(Constants::PERMISSION_DELETE) and parent::unlink($path); } public function file_put_contents($path, $data) { $permissions = $this->file_exists($path) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE; return $this->checkMask($permissions) ? parent::file_put_contents($path, $data) : false; } public function fopen($path, $mode) { if ($mode === 'r' or $mode === 'rb') { return parent::fopen($path, $mode); } else { $permissions = $this->file_exists($path) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE; return $this->checkMask($permissions) ? parent::fopen($path, $mode) : false; } } /** * get a cache instance for the storage * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache * @return \OC\Files\Cache\Cache */ public function getCache($path = '', $storage = null) { if (!$storage) { $storage = $this; } $sourceCache = parent::getCache($path, $storage); return new CachePermissionsMask($sourceCache, $this->mask); } public function getMetaData($path) { $data = parent::getMetaData($path); if ($data && isset($data['permissions'])) { $data['scan_permissions'] = isset($data['scan_permissions']) ? $data['scan_permissions'] : $data['permissions']; $data['permissions'] &= $this->mask; } return $data; } public function getScanner($path = '', $storage = null) { if (!$storage) { $storage = $this->storage; } return parent::getScanner($path, $storage); } } private/Files/Storage/Wrapper/Quota.php 0000604 00000012154 15247130452 0014110 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage\Wrapper; use OCP\Files\Cache\ICacheEntry; class Quota extends Wrapper { /** * @var int $quota */ protected $quota; /** * @var string $sizeRoot */ protected $sizeRoot; /** * @param array $parameters */ public function __construct($parameters) { $this->storage = $parameters['storage']; $this->quota = $parameters['quota']; $this->sizeRoot = isset($parameters['root']) ? $parameters['root'] : ''; } /** * @return int quota value */ public function getQuota() { return $this->quota; } /** * @param string $path * @param \OC\Files\Storage\Storage $storage */ protected function getSize($path, $storage = null) { if (is_null($storage)) { $cache = $this->getCache(); } else { $cache = $storage->getCache(); } $data = $cache->get($path); if ($data instanceof ICacheEntry and isset($data['size'])) { return $data['size']; } else { return \OCP\Files\FileInfo::SPACE_NOT_COMPUTED; } } /** * Get free space as limited by the quota * * @param string $path * @return int */ public function free_space($path) { if ($this->quota < 0) { return $this->storage->free_space($path); } else { $used = $this->getSize($this->sizeRoot); if ($used < 0) { return \OCP\Files\FileInfo::SPACE_NOT_COMPUTED; } else { $free = $this->storage->free_space($path); $quotaFree = max($this->quota - $used, 0); // if free space is known if ($free >= 0) { $free = min($free, $quotaFree); } else { $free = $quotaFree; } return $free; } } } /** * see http://php.net/manual/en/function.file_put_contents.php * * @param string $path * @param string $data * @return bool */ public function file_put_contents($path, $data) { $free = $this->free_space(''); if ($free < 0 or strlen($data) < $free) { return $this->storage->file_put_contents($path, $data); } else { return false; } } /** * see http://php.net/manual/en/function.copy.php * * @param string $source * @param string $target * @return bool */ public function copy($source, $target) { $free = $this->free_space(''); if ($free < 0 or $this->getSize($source) < $free) { return $this->storage->copy($source, $target); } else { return false; } } /** * see http://php.net/manual/en/function.fopen.php * * @param string $path * @param string $mode * @return resource */ public function fopen($path, $mode) { $source = $this->storage->fopen($path, $mode); // don't apply quota for part files if (!$this->isPartFile($path)) { $free = $this->free_space(''); if ($source && $free >= 0 && $mode !== 'r' && $mode !== 'rb') { // only apply quota for files, not metadata, trash or others if (strpos(ltrim($path, '/'), 'files/') === 0) { return \OC\Files\Stream\Quota::wrap($source, $free); } } } return $source; } /** * Checks whether the given path is a part file * * @param string $path Path that may identify a .part file * @return string File path without .part extension * @note this is needed for reusing keys */ private function isPartFile($path) { $extension = pathinfo($path, PATHINFO_EXTENSION); return ($extension === 'part'); } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { $free = $this->free_space(''); if ($free < 0 or $this->getSize($sourceInternalPath, $sourceStorage) < $free) { return $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } else { return false; } } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { $free = $this->free_space(''); if ($free < 0 or $this->getSize($sourceInternalPath, $sourceStorage) < $free) { return $this->storage->moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } else { return false; } } } private/Files/Storage/Wrapper/Jail.php 0000604 00000032170 15247130452 0013676 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage\Wrapper; use OC\Files\Cache\Wrapper\CacheJail; use OC\Files\Cache\Wrapper\JailPropagator; use OCP\Lock\ILockingProvider; /** * Jail to a subdirectory of the wrapped storage * * This restricts access to a subfolder of the wrapped storage with the subfolder becoming the root folder new storage */ class Jail extends Wrapper { /** * @var string */ protected $rootPath; /** * @param array $arguments ['storage' => $storage, 'mask' => $root] * * $storage: The storage that will be wrapper * $root: The folder in the wrapped storage that will become the root folder of the wrapped storage */ public function __construct($arguments) { parent::__construct($arguments); $this->rootPath = $arguments['root']; } public function getUnjailedPath($path) { if ($path === '') { return $this->rootPath; } else { return $this->rootPath . '/' . $path; } } public function getId() { return parent::getId(); } /** * see http://php.net/manual/en/function.mkdir.php * * @param string $path * @return bool */ public function mkdir($path) { return $this->getWrapperStorage()->mkdir($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.rmdir.php * * @param string $path * @return bool */ public function rmdir($path) { return $this->getWrapperStorage()->rmdir($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.opendir.php * * @param string $path * @return resource */ public function opendir($path) { return $this->getWrapperStorage()->opendir($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.is_dir.php * * @param string $path * @return bool */ public function is_dir($path) { return $this->getWrapperStorage()->is_dir($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.is_file.php * * @param string $path * @return bool */ public function is_file($path) { return $this->getWrapperStorage()->is_file($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.stat.php * only the following keys are required in the result: size and mtime * * @param string $path * @return array */ public function stat($path) { return $this->getWrapperStorage()->stat($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.filetype.php * * @param string $path * @return bool */ public function filetype($path) { return $this->getWrapperStorage()->filetype($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.filesize.php * The result for filesize when called on a folder is required to be 0 * * @param string $path * @return int */ public function filesize($path) { return $this->getWrapperStorage()->filesize($this->getUnjailedPath($path)); } /** * check if a file can be created in $path * * @param string $path * @return bool */ public function isCreatable($path) { return $this->getWrapperStorage()->isCreatable($this->getUnjailedPath($path)); } /** * check if a file can be read * * @param string $path * @return bool */ public function isReadable($path) { return $this->getWrapperStorage()->isReadable($this->getUnjailedPath($path)); } /** * check if a file can be written to * * @param string $path * @return bool */ public function isUpdatable($path) { return $this->getWrapperStorage()->isUpdatable($this->getUnjailedPath($path)); } /** * check if a file can be deleted * * @param string $path * @return bool */ public function isDeletable($path) { return $this->getWrapperStorage()->isDeletable($this->getUnjailedPath($path)); } /** * check if a file can be shared * * @param string $path * @return bool */ public function isSharable($path) { return $this->getWrapperStorage()->isSharable($this->getUnjailedPath($path)); } /** * get the full permissions of a path. * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php * * @param string $path * @return int */ public function getPermissions($path) { return $this->getWrapperStorage()->getPermissions($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.file_exists.php * * @param string $path * @return bool */ public function file_exists($path) { return $this->getWrapperStorage()->file_exists($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.filemtime.php * * @param string $path * @return int */ public function filemtime($path) { return $this->getWrapperStorage()->filemtime($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.file_get_contents.php * * @param string $path * @return string */ public function file_get_contents($path) { return $this->getWrapperStorage()->file_get_contents($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.file_put_contents.php * * @param string $path * @param string $data * @return bool */ public function file_put_contents($path, $data) { return $this->getWrapperStorage()->file_put_contents($this->getUnjailedPath($path), $data); } /** * see http://php.net/manual/en/function.unlink.php * * @param string $path * @return bool */ public function unlink($path) { return $this->getWrapperStorage()->unlink($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.rename.php * * @param string $path1 * @param string $path2 * @return bool */ public function rename($path1, $path2) { return $this->getWrapperStorage()->rename($this->getUnjailedPath($path1), $this->getUnjailedPath($path2)); } /** * see http://php.net/manual/en/function.copy.php * * @param string $path1 * @param string $path2 * @return bool */ public function copy($path1, $path2) { return $this->getWrapperStorage()->copy($this->getUnjailedPath($path1), $this->getUnjailedPath($path2)); } /** * see http://php.net/manual/en/function.fopen.php * * @param string $path * @param string $mode * @return resource */ public function fopen($path, $mode) { return $this->getWrapperStorage()->fopen($this->getUnjailedPath($path), $mode); } /** * get the mimetype for a file or folder * The mimetype for a folder is required to be "httpd/unix-directory" * * @param string $path * @return string */ public function getMimeType($path) { return $this->getWrapperStorage()->getMimeType($this->getUnjailedPath($path)); } /** * see http://php.net/manual/en/function.hash.php * * @param string $type * @param string $path * @param bool $raw * @return string */ public function hash($type, $path, $raw = false) { return $this->getWrapperStorage()->hash($type, $this->getUnjailedPath($path), $raw); } /** * see http://php.net/manual/en/function.free_space.php * * @param string $path * @return int */ public function free_space($path) { return $this->getWrapperStorage()->free_space($this->getUnjailedPath($path)); } /** * search for occurrences of $query in file names * * @param string $query * @return array */ public function search($query) { return $this->getWrapperStorage()->search($query); } /** * see http://php.net/manual/en/function.touch.php * If the backend does not support the operation, false should be returned * * @param string $path * @param int $mtime * @return bool */ public function touch($path, $mtime = null) { return $this->getWrapperStorage()->touch($this->getUnjailedPath($path), $mtime); } /** * get the path to a local version of the file. * The local version of the file can be temporary and doesn't have to be persistent across requests * * @param string $path * @return string */ public function getLocalFile($path) { return $this->getWrapperStorage()->getLocalFile($this->getUnjailedPath($path)); } /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @return bool * * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. * returning true for other changes in the folder is optional */ public function hasUpdated($path, $time) { return $this->getWrapperStorage()->hasUpdated($this->getUnjailedPath($path), $time); } /** * get a cache instance for the storage * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache * @return \OC\Files\Cache\Cache */ public function getCache($path = '', $storage = null) { if (!$storage) { $storage = $this->getWrapperStorage(); } $sourceCache = $this->getWrapperStorage()->getCache($this->getUnjailedPath($path), $storage); return new CacheJail($sourceCache, $this->rootPath); } /** * get the user id of the owner of a file or folder * * @param string $path * @return string */ public function getOwner($path) { return $this->getWrapperStorage()->getOwner($this->getUnjailedPath($path)); } /** * get a watcher instance for the cache * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Watcher */ public function getWatcher($path = '', $storage = null) { if (!$storage) { $storage = $this; } return $this->getWrapperStorage()->getWatcher($this->getUnjailedPath($path), $storage); } /** * get the ETag for a file or folder * * @param string $path * @return string */ public function getETag($path) { return $this->getWrapperStorage()->getETag($this->getUnjailedPath($path)); } /** * @param string $path * @return array */ public function getMetaData($path) { return $this->getWrapperStorage()->getMetaData($this->getUnjailedPath($path)); } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider * @throws \OCP\Lock\LockedException */ public function acquireLock($path, $type, ILockingProvider $provider) { $this->getWrapperStorage()->acquireLock($this->getUnjailedPath($path), $type, $provider); } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider */ public function releaseLock($path, $type, ILockingProvider $provider) { $this->getWrapperStorage()->releaseLock($this->getUnjailedPath($path), $type, $provider); } /** * @param string $path * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param \OCP\Lock\ILockingProvider $provider */ public function changeLock($path, $type, ILockingProvider $provider) { $this->getWrapperStorage()->changeLock($this->getUnjailedPath($path), $type, $provider); } /** * Resolve the path for the source of the share * * @param string $path * @return array */ public function resolvePath($path) { return [$this->getWrapperStorage(), $this->getUnjailedPath($path)]; } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { if ($sourceStorage === $this) { return $this->copy($sourceInternalPath, $targetInternalPath); } return $this->getWrapperStorage()->copyFromStorage($sourceStorage, $sourceInternalPath, $this->getUnjailedPath($targetInternalPath)); } /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { if ($sourceStorage === $this) { return $this->rename($sourceInternalPath, $targetInternalPath); } return $this->getWrapperStorage()->moveFromStorage($sourceStorage, $sourceInternalPath, $this->getUnjailedPath($targetInternalPath)); } public function getPropagator($storage = null) { if (isset($this->propagator)) { return $this->propagator; } if (!$storage) { $storage = $this; } $this->propagator = new JailPropagator($storage, \OC::$server->getDatabaseConnection()); return $this->propagator; } } private/Files/Storage/StorageFactory.php 0000604 00000007047 15247130452 0014340 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage\IStorageFactory; class StorageFactory implements IStorageFactory { /** * @var array[] [$name=>['priority'=>$priority, 'wrapper'=>$callable] $storageWrappers */ private $storageWrappers = []; /** * allow modifier storage behaviour by adding wrappers around storages * * $callback should be a function of type (string $mountPoint, Storage $storage) => Storage * * @param string $wrapperName name of the wrapper * @param callable $callback callback * @param int $priority wrappers with the lower priority are applied last (meaning they get called first) * @param \OCP\Files\Mount\IMountPoint[] $existingMounts existing mount points to apply the wrapper to * @return bool true if the wrapper was added, false if there was already a wrapper with this * name registered */ public function addStorageWrapper($wrapperName, $callback, $priority = 50, $existingMounts = []) { if (isset($this->storageWrappers[$wrapperName])) { return false; } // apply to existing mounts before registering it to prevent applying it double in MountPoint::createStorage foreach ($existingMounts as $mount) { $mount->wrapStorage($callback); } $this->storageWrappers[$wrapperName] = ['wrapper' => $callback, 'priority' => $priority]; return true; } /** * Remove a storage wrapper by name. * Note: internal method only to be used for cleanup * * @param string $wrapperName name of the wrapper * @internal */ public function removeStorageWrapper($wrapperName) { unset($this->storageWrappers[$wrapperName]); } /** * Create an instance of a storage and apply the registered storage wrappers * * @param \OCP\Files\Mount\IMountPoint $mountPoint * @param string $class * @param array $arguments * @return \OCP\Files\Storage */ public function getInstance(IMountPoint $mountPoint, $class, $arguments) { return $this->wrap($mountPoint, new $class($arguments)); } /** * @param \OCP\Files\Mount\IMountPoint $mountPoint * @param \OCP\Files\Storage $storage * @return \OCP\Files\Storage */ public function wrap(IMountPoint $mountPoint, $storage) { $wrappers = array_values($this->storageWrappers); usort($wrappers, function ($a, $b) { return $b['priority'] - $a['priority']; }); /** @var callable[] $wrappers */ $wrappers = array_map(function ($wrapper) { return $wrapper['wrapper']; }, $wrappers); foreach ($wrappers as $wrapper) { $storage = $wrapper($mountPoint->getMountPoint(), $storage, $mountPoint); if (!($storage instanceof \OCP\Files\Storage)) { throw new \Exception('Invalid result from storage wrapper'); } } return $storage; } } private/Files/Storage/Home.php 0000604 00000005231 15247130452 0012265 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; use OC\Files\Cache\HomePropagator; /** * Specialized version of Local storage for home directory usage */ class Home extends Local implements \OCP\Files\IHomeStorage { /** * @var string */ protected $id; /** * @var \OC\User\User $user */ protected $user; /** * Construct a Home storage instance * @param array $arguments array with "user" containing the * storage owner */ public function __construct($arguments) { $this->user = $arguments['user']; $datadir = $this->user->getHome(); $this->id = 'home::' . $this->user->getUID(); parent::__construct(array('datadir' => $datadir)); } public function getId() { return $this->id; } /** * @return \OC\Files\Cache\HomeCache */ public function getCache($path = '', $storage = null) { if (!$storage) { $storage = $this; } if (!isset($this->cache)) { $this->cache = new \OC\Files\Cache\HomeCache($storage); } return $this->cache; } /** * get a propagator instance for the cache * * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Propagator */ public function getPropagator($storage = null) { if (!$storage) { $storage = $this; } if (!isset($this->propagator)) { $this->propagator = new HomePropagator($storage, \OC::$server->getDatabaseConnection()); } return $this->propagator; } /** * Returns the owner of this home storage * @return \OC\User\User owner of this home storage */ public function getUser() { return $this->user; } /** * get the owner of a path * * @param string $path The path to get the owner * @return string uid or false */ public function getOwner($path) { return $this->user->getUID(); } } private/Files/Storage/CommonTest.php 0000604 00000004670 15247130452 0013473 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Felix Moeller <mail@felixmoeller.de> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * test implementation for \OC\Files\Storage\Common with \OC\Files\Storage\Local */ namespace OC\Files\Storage; class CommonTest extends \OC\Files\Storage\Common{ /** * underlying local storage used for missing functions * @var \OC\Files\Storage\Local */ private $storage; public function __construct($params) { $this->storage=new \OC\Files\Storage\Local($params); } public function getId(){ return 'test::'.$this->storage->getId(); } public function mkdir($path) { return $this->storage->mkdir($path); } public function rmdir($path) { return $this->storage->rmdir($path); } public function opendir($path) { return $this->storage->opendir($path); } public function stat($path) { return $this->storage->stat($path); } public function filetype($path) { return @$this->storage->filetype($path); } public function isReadable($path) { return $this->storage->isReadable($path); } public function isUpdatable($path) { return $this->storage->isUpdatable($path); } public function file_exists($path) { return $this->storage->file_exists($path); } public function unlink($path) { return $this->storage->unlink($path); } public function fopen($path, $mode) { return $this->storage->fopen($path, $mode); } public function free_space($path) { return $this->storage->free_space($path); } public function touch($path, $mtime=null) { return $this->storage->touch($path, $mtime); } } private/Files/Storage/DAV.php 0000604 00000056634 15247130452 0012024 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Carlos Cerrillo <ccerrillo@gmail.com> * @author Felix Moeller <mail@felixmoeller.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Philipp Kapfer <philipp.kapfer@gmx.at> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; use Exception; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Message\ResponseInterface; use Icewind\Streams\CallbackWrapper; use OC\Files\Filesystem; use Icewind\Streams\IteratorDirectory; use OC\MemCache\ArrayCache; use OCP\AppFramework\Http; use OCP\Constants; use OCP\Files\FileInfo; use OCP\Files\StorageInvalidException; use OCP\Files\StorageNotAvailableException; use OCP\Util; use Sabre\DAV\Client; use Sabre\DAV\Xml\Property\ResourceType; use Sabre\HTTP\ClientException; use Sabre\HTTP\ClientHttpException; /** * Class DAV * * @package OC\Files\Storage */ class DAV extends Common { /** @var string */ protected $password; /** @var string */ protected $user; /** @var string */ protected $authType; /** @var string */ protected $host; /** @var bool */ protected $secure; /** @var string */ protected $root; /** @var string */ protected $certPath; /** @var bool */ protected $ready; /** @var Client */ protected $client; /** @var ArrayCache */ protected $statCache; /** @var \OCP\Http\Client\IClientService */ protected $httpClientService; /** * @param array $params * @throws \Exception */ public function __construct($params) { $this->statCache = new ArrayCache(); $this->httpClientService = \OC::$server->getHTTPClientService(); if (isset($params['host']) && isset($params['user']) && isset($params['password'])) { $host = $params['host']; //remove leading http[s], will be generated in createBaseUri() if (substr($host, 0, 8) == "https://") $host = substr($host, 8); else if (substr($host, 0, 7) == "http://") $host = substr($host, 7); $this->host = $host; $this->user = $params['user']; $this->password = $params['password']; if (isset($params['authType'])) { $this->authType = $params['authType']; } if (isset($params['secure'])) { if (is_string($params['secure'])) { $this->secure = ($params['secure'] === 'true'); } else { $this->secure = (bool)$params['secure']; } } else { $this->secure = false; } if ($this->secure === true) { // inject mock for testing $certManager = \OC::$server->getCertificateManager(); if (is_null($certManager)) { //no user $certManager = \OC::$server->getCertificateManager(null); } $certPath = $certManager->getAbsoluteBundlePath(); if (file_exists($certPath)) { $this->certPath = $certPath; } } $this->root = isset($params['root']) ? $params['root'] : '/'; if (!$this->root || $this->root[0] != '/') { $this->root = '/' . $this->root; } if (substr($this->root, -1, 1) != '/') { $this->root .= '/'; } } else { throw new \Exception('Invalid webdav storage configuration'); } } protected function init() { if ($this->ready) { return; } $this->ready = true; $settings = [ 'baseUri' => $this->createBaseUri(), 'userName' => $this->user, 'password' => $this->password, ]; if (isset($this->authType)) { $settings['authType'] = $this->authType; } $proxy = \OC::$server->getConfig()->getSystemValue('proxy', ''); if($proxy !== '') { $settings['proxy'] = $proxy; } $this->client = new Client($settings); $this->client->setThrowExceptions(true); if ($this->secure === true && $this->certPath) { $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath); } } /** * Clear the stat cache */ public function clearStatCache() { $this->statCache->clear(); } /** {@inheritdoc} */ public function getId() { return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root; } /** {@inheritdoc} */ public function createBaseUri() { $baseUri = 'http'; if ($this->secure) { $baseUri .= 's'; } $baseUri .= '://' . $this->host . $this->root; return $baseUri; } /** {@inheritdoc} */ public function mkdir($path) { $this->init(); $path = $this->cleanPath($path); $result = $this->simpleResponse('MKCOL', $path, null, 201); if ($result) { $this->statCache->set($path, true); } return $result; } /** {@inheritdoc} */ public function rmdir($path) { $this->init(); $path = $this->cleanPath($path); // FIXME: some WebDAV impl return 403 when trying to DELETE // a non-empty folder $result = $this->simpleResponse('DELETE', $path . '/', null, 204); $this->statCache->clear($path . '/'); $this->statCache->remove($path); return $result; } /** {@inheritdoc} */ public function opendir($path) { $this->init(); $path = $this->cleanPath($path); try { $response = $this->client->propFind( $this->encodePath($path), ['{DAV:}href'], 1 ); if ($response === false) { return false; } $content = []; $files = array_keys($response); array_shift($files); //the first entry is the current directory if (!$this->statCache->hasKey($path)) { $this->statCache->set($path, true); } foreach ($files as $file) { $file = urldecode($file); // do not store the real entry, we might not have all properties if (!$this->statCache->hasKey($path)) { $this->statCache->set($file, true); } $file = basename($file); $content[] = $file; } return IteratorDirectory::wrap($content); } catch (\Exception $e) { $this->convertException($e, $path); } return false; } /** * Propfind call with cache handling. * * First checks if information is cached. * If not, request it from the server then store to cache. * * @param string $path path to propfind * * @return array|boolean propfind response or false if the entry was not found * * @throws ClientHttpException */ protected function propfind($path) { $path = $this->cleanPath($path); $cachedResponse = $this->statCache->get($path); // we either don't know it, or we know it exists but need more details if (is_null($cachedResponse) || $cachedResponse === true) { $this->init(); try { $response = $this->client->propFind( $this->encodePath($path), array( '{DAV:}getlastmodified', '{DAV:}getcontentlength', '{DAV:}getcontenttype', '{http://owncloud.org/ns}permissions', '{http://open-collaboration-services.org/ns}share-permissions', '{DAV:}resourcetype', '{DAV:}getetag', ) ); $this->statCache->set($path, $response); } catch (ClientHttpException $e) { if ($e->getHttpStatus() === 404) { $this->statCache->clear($path . '/'); $this->statCache->set($path, false); return false; } $this->convertException($e, $path); } catch (\Exception $e) { $this->convertException($e, $path); } } else { $response = $cachedResponse; } return $response; } /** {@inheritdoc} */ public function filetype($path) { try { $response = $this->propfind($path); if ($response === false) { return false; } $responseType = []; if (isset($response["{DAV:}resourcetype"])) { /** @var ResourceType[] $response */ $responseType = $response["{DAV:}resourcetype"]->getValue(); } return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; } catch (\Exception $e) { $this->convertException($e, $path); } return false; } /** {@inheritdoc} */ public function file_exists($path) { try { $path = $this->cleanPath($path); $cachedState = $this->statCache->get($path); if ($cachedState === false) { // we know the file doesn't exist return false; } else if (!is_null($cachedState)) { return true; } // need to get from server return ($this->propfind($path) !== false); } catch (\Exception $e) { $this->convertException($e, $path); } return false; } /** {@inheritdoc} */ public function unlink($path) { $this->init(); $path = $this->cleanPath($path); $result = $this->simpleResponse('DELETE', $path, null, 204); $this->statCache->clear($path . '/'); $this->statCache->remove($path); return $result; } /** {@inheritdoc} */ public function fopen($path, $mode) { $this->init(); $path = $this->cleanPath($path); switch ($mode) { case 'r': case 'rb': try { $response = $this->httpClientService ->newClient() ->get($this->createBaseUri() . $this->encodePath($path), [ 'auth' => [$this->user, $this->password], 'stream' => true ]); } catch (RequestException $e) { if ($e->getResponse() instanceof ResponseInterface && $e->getResponse()->getStatusCode() === 404) { return false; } else { throw $e; } } if ($response->getStatusCode() !== Http::STATUS_OK) { if ($response->getStatusCode() === Http::STATUS_LOCKED) { throw new \OCP\Lock\LockedException($path); } else { Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR); } } return $response->getBody(); case 'w': case 'wb': case 'a': case 'ab': case 'r+': case 'w+': case 'wb+': case 'a+': case 'x': case 'x+': case 'c': case 'c+': //emulate these $tempManager = \OC::$server->getTempManager(); if (strrpos($path, '.') !== false) { $ext = substr($path, strrpos($path, '.')); } else { $ext = ''; } if ($this->file_exists($path)) { if (!$this->isUpdatable($path)) { return false; } if ($mode === 'w' or $mode === 'w+') { $tmpFile = $tempManager->getTemporaryFile($ext); } else { $tmpFile = $this->getCachedFile($path); } } else { if (!$this->isCreatable(dirname($path))) { return false; } $tmpFile = $tempManager->getTemporaryFile($ext); } $handle = fopen($tmpFile, $mode); return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { $this->writeBack($tmpFile, $path); }); } } /** * @param string $tmpFile */ public function writeBack($tmpFile, $path) { $this->uploadFile($tmpFile, $path); unlink($tmpFile); } /** {@inheritdoc} */ public function free_space($path) { $this->init(); $path = $this->cleanPath($path); try { // TODO: cacheable ? $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']); if ($response === false) { return FileInfo::SPACE_UNKNOWN; } if (isset($response['{DAV:}quota-available-bytes'])) { return (int)$response['{DAV:}quota-available-bytes']; } else { return FileInfo::SPACE_UNKNOWN; } } catch (\Exception $e) { return FileInfo::SPACE_UNKNOWN; } } /** {@inheritdoc} */ public function touch($path, $mtime = null) { $this->init(); if (is_null($mtime)) { $mtime = time(); } $path = $this->cleanPath($path); // if file exists, update the mtime, else create a new empty file if ($this->file_exists($path)) { try { $this->statCache->remove($path); $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]); // non-owncloud clients might not have accepted the property, need to recheck it $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0); if ($response === false) { return false; } if (isset($response['{DAV:}getlastmodified'])) { $remoteMtime = strtotime($response['{DAV:}getlastmodified']); if ($remoteMtime !== $mtime) { // server has not accepted the mtime return false; } } } catch (ClientHttpException $e) { if ($e->getHttpStatus() === 501) { return false; } $this->convertException($e, $path); return false; } catch (\Exception $e) { $this->convertException($e, $path); return false; } } else { $this->file_put_contents($path, ''); } return true; } /** * @param string $path * @param string $data * @return int */ public function file_put_contents($path, $data) { $path = $this->cleanPath($path); $result = parent::file_put_contents($path, $data); $this->statCache->remove($path); return $result; } /** * @param string $path * @param string $target */ protected function uploadFile($path, $target) { $this->init(); // invalidate $target = $this->cleanPath($target); $this->statCache->remove($target); $source = fopen($path, 'r'); $this->httpClientService ->newClient() ->put($this->createBaseUri() . $this->encodePath($target), [ 'body' => $source, 'auth' => [$this->user, $this->password] ]); $this->removeCachedFile($target); } /** {@inheritdoc} */ public function rename($path1, $path2) { $this->init(); $path1 = $this->cleanPath($path1); $path2 = $this->cleanPath($path2); try { // overwrite directory ? if ($this->is_dir($path2)) { // needs trailing slash in destination $path2 = rtrim($path2, '/') . '/'; } $this->client->request( 'MOVE', $this->encodePath($path1), null, [ 'Destination' => $this->createBaseUri() . $this->encodePath($path2), ] ); $this->statCache->clear($path1 . '/'); $this->statCache->clear($path2 . '/'); $this->statCache->set($path1, false); $this->statCache->set($path2, true); $this->removeCachedFile($path1); $this->removeCachedFile($path2); return true; } catch (\Exception $e) { $this->convertException($e); } return false; } /** {@inheritdoc} */ public function copy($path1, $path2) { $this->init(); $path1 = $this->cleanPath($path1); $path2 = $this->cleanPath($path2); try { // overwrite directory ? if ($this->is_dir($path2)) { // needs trailing slash in destination $path2 = rtrim($path2, '/') . '/'; } $this->client->request( 'COPY', $this->encodePath($path1), null, [ 'Destination' => $this->createBaseUri() . $this->encodePath($path2), ] ); $this->statCache->clear($path2 . '/'); $this->statCache->set($path2, true); $this->removeCachedFile($path2); return true; } catch (\Exception $e) { $this->convertException($e); } return false; } /** {@inheritdoc} */ public function stat($path) { try { $response = $this->propfind($path); if (!$response) { return false; } return [ 'mtime' => strtotime($response['{DAV:}getlastmodified']), 'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0, ]; } catch (\Exception $e) { $this->convertException($e, $path); } return array(); } /** {@inheritdoc} */ public function getMimeType($path) { try { $response = $this->propfind($path); if ($response === false) { return false; } $responseType = []; if (isset($response["{DAV:}resourcetype"])) { /** @var ResourceType[] $response */ $responseType = $response["{DAV:}resourcetype"]->getValue(); } $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; if ($type == 'dir') { return 'httpd/unix-directory'; } elseif (isset($response['{DAV:}getcontenttype'])) { return $response['{DAV:}getcontenttype']; } else { return false; } } catch (\Exception $e) { $this->convertException($e, $path); } return false; } /** * @param string $path * @return string */ public function cleanPath($path) { if ($path === '') { return $path; } $path = Filesystem::normalizePath($path); // remove leading slash return substr($path, 1); } /** * URL encodes the given path but keeps the slashes * * @param string $path to encode * @return string encoded path */ protected function encodePath($path) { // slashes need to stay return str_replace('%2F', '/', rawurlencode($path)); } /** * @param string $method * @param string $path * @param string|resource|null $body * @param int $expected * @return bool * @throws StorageInvalidException * @throws StorageNotAvailableException */ protected function simpleResponse($method, $path, $body, $expected) { $path = $this->cleanPath($path); try { $response = $this->client->request($method, $this->encodePath($path), $body); return $response['statusCode'] == $expected; } catch (ClientHttpException $e) { if ($e->getHttpStatus() === 404 && $method === 'DELETE') { $this->statCache->clear($path . '/'); $this->statCache->set($path, false); return false; } $this->convertException($e, $path); } catch (\Exception $e) { $this->convertException($e, $path); } return false; } /** * check if curl is installed */ public static function checkDependencies() { return true; } /** {@inheritdoc} */ public function isUpdatable($path) { return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE); } /** {@inheritdoc} */ public function isCreatable($path) { return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE); } /** {@inheritdoc} */ public function isSharable($path) { return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE); } /** {@inheritdoc} */ public function isDeletable($path) { return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE); } /** {@inheritdoc} */ public function getPermissions($path) { $this->init(); $path = $this->cleanPath($path); $response = $this->propfind($path); if ($response === false) { return 0; } if (isset($response['{http://owncloud.org/ns}permissions'])) { return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']); } else if ($this->is_dir($path)) { return Constants::PERMISSION_ALL; } else if ($this->file_exists($path)) { return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE; } else { return 0; } } /** {@inheritdoc} */ public function getETag($path) { $this->init(); $path = $this->cleanPath($path); $response = $this->propfind($path); if ($response === false) { return null; } if (isset($response['{DAV:}getetag'])) { return trim($response['{DAV:}getetag'], '"'); } return parent::getEtag($path); } /** * @param string $permissionsString * @return int */ protected function parsePermissions($permissionsString) { $permissions = Constants::PERMISSION_READ; if (strpos($permissionsString, 'R') !== false) { $permissions |= Constants::PERMISSION_SHARE; } if (strpos($permissionsString, 'D') !== false) { $permissions |= Constants::PERMISSION_DELETE; } if (strpos($permissionsString, 'W') !== false) { $permissions |= Constants::PERMISSION_UPDATE; } if (strpos($permissionsString, 'CK') !== false) { $permissions |= Constants::PERMISSION_CREATE; $permissions |= Constants::PERMISSION_UPDATE; } return $permissions; } /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @throws \OCP\Files\StorageNotAvailableException * @return bool */ public function hasUpdated($path, $time) { $this->init(); $path = $this->cleanPath($path); try { // force refresh for $path $this->statCache->remove($path); $response = $this->propfind($path); if ($response === false) { if ($path === '') { // if root is gone it means the storage is not available throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } return false; } if (isset($response['{DAV:}getetag'])) { $cachedData = $this->getCache()->get($path); $etag = null; if (isset($response['{DAV:}getetag'])) { $etag = trim($response['{DAV:}getetag'], '"'); } if (!empty($etag) && $cachedData['etag'] !== $etag) { return true; } else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) { $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions']; return $sharePermissions !== $cachedData['permissions']; } else if (isset($response['{http://owncloud.org/ns}permissions'])) { $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']); return $permissions !== $cachedData['permissions']; } else { return false; } } else { $remoteMtime = strtotime($response['{DAV:}getlastmodified']); return $remoteMtime > $time; } } catch (ClientHttpException $e) { if ($e->getHttpStatus() === 405) { if ($path === '') { // if root is gone it means the storage is not available throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } return false; } $this->convertException($e, $path); return false; } catch (\Exception $e) { $this->convertException($e, $path); return false; } } /** * Interpret the given exception and decide whether it is due to an * unavailable storage, invalid storage or other. * This will either throw StorageInvalidException, StorageNotAvailableException * or do nothing. * * @param Exception $e sabre exception * @param string $path optional path from the operation * * @throws StorageInvalidException if the storage is invalid, for example * when the authentication expired or is invalid * @throws StorageNotAvailableException if the storage is not available, * which might be temporary */ protected function convertException(Exception $e, $path = '') { \OC::$server->getLogger()->logException($e); Util::writeLog('files_external', $e->getMessage(), Util::ERROR); if ($e instanceof ClientHttpException) { if ($e->getHttpStatus() === Http::STATUS_LOCKED) { throw new \OCP\Lock\LockedException($path); } if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) { // either password was changed or was invalid all along throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage()); } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) { // ignore exception for MethodNotAllowed, false will be returned return; } throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } else if ($e instanceof ClientException) { // connection timeout or refused, server could be temporarily down throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } else if ($e instanceof \InvalidArgumentException) { // parse error because the server returned HTML instead of XML, // possibly temporarily down throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) { // rethrow throw $e; } // TODO: only log for now, but in the future need to wrap/rethrow exception } } private/Files/Storage/Temporary.php 0000604 00000002563 15247130452 0013364 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; /** * local storage backend in temporary folder for testing purpose */ class Temporary extends Local{ public function __construct($arguments = null) { parent::__construct(array('datadir' => \OC::$server->getTempManager()->getTemporaryFolder())); } public function cleanUp() { \OC_Helper::rmdirr($this->datadir); } public function __destruct() { parent::__destruct(); $this->cleanUp(); } public function getDataDir() { return $this->datadir; } } private/Files/Storage/Flysystem.php 0000604 00000013204 15247130452 0013373 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use League\Flysystem\AdapterInterface; use League\Flysystem\FileNotFoundException; use League\Flysystem\Filesystem; use League\Flysystem\Plugin\GetWithMetadata; /** * Generic adapter between flysystem adapters and owncloud's storage system * * To use: subclass and call $this->buildFlysystem with the flysystem adapter of choice */ abstract class Flysystem extends Common { /** * @var Filesystem */ protected $flysystem; /** * @var string */ protected $root = ''; /** * Initialize the storage backend with a flyssytem adapter * * @param \League\Flysystem\AdapterInterface $adapter */ protected function buildFlySystem(AdapterInterface $adapter) { $this->flysystem = new Filesystem($adapter); $this->flysystem->addPlugin(new GetWithMetadata()); } protected function buildPath($path) { $fullPath = \OC\Files\Filesystem::normalizePath($this->root . '/' . $path); return ltrim($fullPath, '/'); } /** * {@inheritdoc} */ public function file_get_contents($path) { return $this->flysystem->read($this->buildPath($path)); } /** * {@inheritdoc} */ public function file_put_contents($path, $data) { return $this->flysystem->put($this->buildPath($path), $data); } /** * {@inheritdoc} */ public function file_exists($path) { return $this->flysystem->has($this->buildPath($path)); } /** * {@inheritdoc} */ public function unlink($path) { if ($this->is_dir($path)) { return $this->rmdir($path); } try { return $this->flysystem->delete($this->buildPath($path)); } catch (FileNotFoundException $e) { return false; } } /** * {@inheritdoc} */ public function rename($source, $target) { if ($this->file_exists($target)) { $this->unlink($target); } return $this->flysystem->rename($this->buildPath($source), $this->buildPath($target)); } /** * {@inheritdoc} */ public function copy($source, $target) { if ($this->file_exists($target)) { $this->unlink($target); } return $this->flysystem->copy($this->buildPath($source), $this->buildPath($target)); } /** * {@inheritdoc} */ public function filesize($path) { if ($this->is_dir($path)) { return 0; } else { return $this->flysystem->getSize($this->buildPath($path)); } } /** * {@inheritdoc} */ public function mkdir($path) { if ($this->file_exists($path)) { return false; } return $this->flysystem->createDir($this->buildPath($path)); } /** * {@inheritdoc} */ public function filemtime($path) { return $this->flysystem->getTimestamp($this->buildPath($path)); } /** * {@inheritdoc} */ public function rmdir($path) { try { return @$this->flysystem->deleteDir($this->buildPath($path)); } catch (FileNotFoundException $e) { return false; } } /** * {@inheritdoc} */ public function opendir($path) { try { $content = $this->flysystem->listContents($this->buildPath($path)); } catch (FileNotFoundException $e) { return false; } $names = array_map(function ($object) { return $object['basename']; }, $content); return IteratorDirectory::wrap($names); } /** * {@inheritdoc} */ public function fopen($path, $mode) { $fullPath = $this->buildPath($path); $useExisting = true; switch ($mode) { case 'r': case 'rb': try { return $this->flysystem->readStream($fullPath); } catch (FileNotFoundException $e) { return false; } case 'w': case 'w+': case 'wb': case 'wb+': $useExisting = false; case 'a': case 'ab': case 'r+': case 'a+': case 'x': case 'x+': case 'c': case 'c+': //emulate these if ($useExisting and $this->file_exists($path)) { if (!$this->isUpdatable($path)) { return false; } $tmpFile = $this->getCachedFile($path); } else { if (!$this->isCreatable(dirname($path))) { return false; } $tmpFile = \OCP\Files::tmpFile(); } $source = fopen($tmpFile, $mode); return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath) { $this->flysystem->putStream($fullPath, fopen($tmpFile, 'r')); unlink($tmpFile); }); } return false; } /** * {@inheritdoc} */ public function touch($path, $mtime = null) { if ($this->file_exists($path)) { return false; } else { $this->file_put_contents($path, ''); return true; } } /** * {@inheritdoc} */ public function stat($path) { $info = $this->flysystem->getWithMetadata($this->buildPath($path), ['timestamp', 'size']); return [ 'mtime' => $info['timestamp'], 'size' => $info['size'] ]; } /** * {@inheritdoc} */ public function filetype($path) { if ($path === '' or $path === '/' or $path === '.') { return 'dir'; } try { $info = $this->flysystem->getMetadata($this->buildPath($path)); } catch (FileNotFoundException $e) { return false; } return $info['type']; } } private/Files/Storage/FailedStorage.php 0000604 00000016315 15247130452 0014113 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Storage; use OC\Files\Cache\FailedCache; use \OCP\Lock\ILockingProvider; use \OCP\Files\StorageNotAvailableException; /** * Storage placeholder to represent a missing precondition, storage unavailable */ class FailedStorage extends Common { /** @var \Exception */ protected $e; /** * @param array $params ['exception' => \Exception] */ public function __construct($params) { $this->e = $params['exception']; if (!$this->e) { throw new \InvalidArgumentException('Missing "exception" argument in FailedStorage constructor'); } } public function getId() { // we can't return anything sane here return 'failedstorage'; } public function mkdir($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function rmdir($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function opendir($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function is_dir($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function is_file($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function stat($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function filetype($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function filesize($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function isCreatable($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function isReadable($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function isUpdatable($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function isDeletable($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function isSharable($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function getPermissions($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function file_exists($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function filemtime($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function file_get_contents($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function file_put_contents($path, $data) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function unlink($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function rename($path1, $path2) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function copy($path1, $path2) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function fopen($path, $mode) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function getMimeType($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function hash($type, $path, $raw = false) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function free_space($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function search($query) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function touch($path, $mtime = null) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function getLocalFile($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function getLocalFolder($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function hasUpdated($path, $time) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function getETag($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function getDirectDownload($path) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function verifyPath($path, $fileName) { return true; } public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function acquireLock($path, $type, ILockingProvider $provider) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function releaseLock($path, $type, ILockingProvider $provider) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function changeLock($path, $type, ILockingProvider $provider) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function getAvailability() { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function setAvailability($isAvailable) { throw new StorageNotAvailableException($this->e->getMessage(), $this->e->getCode(), $this->e); } public function getCache($path = '', $storage = null) { return new FailedCache(); } } private/Files/Config/MountProviderCollection.php 0000604 00000012330 15247130452 0016025 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Config; use OC\Hooks\Emitter; use OC\Hooks\EmitterTrait; use OCP\Files\Config\IHomeMountProvider; use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Config\IMountProvider; use OCP\Files\Config\IUserMountCache; use OCP\Files\Mount\IMountManager; use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage\IStorageFactory; use OCP\IUser; class MountProviderCollection implements IMountProviderCollection, Emitter { use EmitterTrait; /** * @var \OCP\Files\Config\IHomeMountProvider[] */ private $homeProviders = []; /** * @var \OCP\Files\Config\IMountProvider[] */ private $providers = array(); /** * @var \OCP\Files\Storage\IStorageFactory */ private $loader; /** * @var \OCP\Files\Config\IUserMountCache */ private $mountCache; /** * @param \OCP\Files\Storage\IStorageFactory $loader * @param IUserMountCache $mountCache */ public function __construct(IStorageFactory $loader, IUserMountCache $mountCache) { $this->loader = $loader; $this->mountCache = $mountCache; } /** * Get all configured mount points for the user * * @param \OCP\IUser $user * @return \OCP\Files\Mount\IMountPoint[] */ public function getMountsForUser(IUser $user) { $loader = $this->loader; $mounts = array_map(function (IMountProvider $provider) use ($user, $loader) { return $provider->getMountsForUser($user, $loader); }, $this->providers); $mounts = array_filter($mounts, function ($result) { return is_array($result); }); return array_reduce($mounts, function (array $mounts, array $providerMounts) { return array_merge($mounts, $providerMounts); }, array()); } public function addMountForUser(IUser $user, IMountManager $mountManager) { // shared mount provider gets to go last since it needs to know existing files // to check for name collisions $firstMounts = []; $firstProviders = array_filter($this->providers, function (IMountProvider $provider) { return (get_class($provider) !== 'OCA\Files_Sharing\MountProvider'); }); $lastProviders = array_filter($this->providers, function (IMountProvider $provider) { return (get_class($provider) === 'OCA\Files_Sharing\MountProvider'); }); foreach ($firstProviders as $provider) { $mounts = $provider->getMountsForUser($user, $this->loader); if (is_array($mounts)) { $firstMounts = array_merge($firstMounts, $mounts); } } array_walk($firstMounts, [$mountManager, 'addMount']); $lateMounts = []; foreach ($lastProviders as $provider) { $mounts = $provider->getMountsForUser($user, $this->loader); if (is_array($mounts)) { $lateMounts = array_merge($lateMounts, $mounts); } } array_walk($lateMounts, [$mountManager, 'addMount']); return array_merge($lateMounts, $firstMounts); } /** * Get the configured home mount for this user * * @param \OCP\IUser $user * @return \OCP\Files\Mount\IMountPoint * @since 9.1.0 */ public function getHomeMountForUser(IUser $user) { /** @var \OCP\Files\Config\IHomeMountProvider[] $providers */ $providers = array_reverse($this->homeProviders); // call the latest registered provider first to give apps an opportunity to overwrite builtin foreach ($providers as $homeProvider) { if ($mount = $homeProvider->getHomeMountForUser($user, $this->loader)) { $mount->setMountPoint('/' . $user->getUID()); //make sure the mountpoint is what we expect return $mount; } } throw new \Exception('No home storage configured for user ' . $user); } /** * Add a provider for mount points * * @param \OCP\Files\Config\IMountProvider $provider */ public function registerProvider(IMountProvider $provider) { $this->providers[] = $provider; $this->emit('\OC\Files\Config', 'registerMountProvider', [$provider]); } /** * Add a provider for home mount points * * @param \OCP\Files\Config\IHomeMountProvider $provider * @since 9.1.0 */ public function registerHomeProvider(IHomeMountProvider $provider) { $this->homeProviders[] = $provider; $this->emit('\OC\Files\Config', 'registerHomeMountProvider', [$provider]); } /** * Cache mounts for user * * @param IUser $user * @param IMountPoint[] $mountPoints */ public function registerMounts(IUser $user, array $mountPoints) { $this->mountCache->registerMounts($user, $mountPoints); } /** * Get the mount cache which can be used to search for mounts without setting up the filesystem * * @return IUserMountCache */ public function getMountCache() { return $this->mountCache; } } private/Files/Config/UserMountCache.php 0000604 00000026106 15247130452 0014067 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Config; use OCA\Files_Sharing\SharedMount; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IUserMountCache; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\ICache; use OCP\IDBConnection; use OCP\ILogger; use OCP\IUser; use OCP\IUserManager; use OC\Cache\CappedMemoryCache; /** * Cache mounts points per user in the cache so we can easilly look them up */ class UserMountCache implements IUserMountCache { /** * @var IDBConnection */ private $connection; /** * @var IUserManager */ private $userManager; /** * Cached mount info. * Map of $userId to ICachedMountInfo. * * @var ICache **/ private $mountsForUsers; /** * @var ILogger */ private $logger; /** * @var ICache */ private $cacheInfoCache; /** * UserMountCache constructor. * * @param IDBConnection $connection * @param IUserManager $userManager * @param ILogger $logger */ public function __construct(IDBConnection $connection, IUserManager $userManager, ILogger $logger) { $this->connection = $connection; $this->userManager = $userManager; $this->logger = $logger; $this->cacheInfoCache = new CappedMemoryCache(); $this->mountsForUsers = new CappedMemoryCache(); } public function registerMounts(IUser $user, array $mounts) { // filter out non-proper storages coming from unit tests $mounts = array_filter($mounts, function (IMountPoint $mount) { return $mount instanceof SharedMount || $mount->getStorage() && $mount->getStorage()->getCache(); }); /** @var ICachedMountInfo[] $newMounts */ $newMounts = array_map(function (IMountPoint $mount) use ($user) { // filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet) if ($mount->getStorageRootId() === -1) { return null; } else { return new LazyStorageMountInfo($user, $mount); } }, $mounts); $newMounts = array_values(array_filter($newMounts)); $cachedMounts = $this->getMountsForUser($user); $mountDiff = function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) { // since we are only looking for mounts for a specific user comparing on root id is enough return $mount1->getRootId() - $mount2->getRootId(); }; /** @var ICachedMountInfo[] $addedMounts */ $addedMounts = array_udiff($newMounts, $cachedMounts, $mountDiff); /** @var ICachedMountInfo[] $removedMounts */ $removedMounts = array_udiff($cachedMounts, $newMounts, $mountDiff); $changedMounts = $this->findChangedMounts($newMounts, $cachedMounts); foreach ($addedMounts as $mount) { $this->addToCache($mount); $this->mountsForUsers[$user->getUID()][] = $mount; } foreach ($removedMounts as $mount) { $this->removeFromCache($mount); $index = array_search($mount, $this->mountsForUsers[$user->getUID()]); unset($this->mountsForUsers[$user->getUID()][$index]); } foreach ($changedMounts as $mount) { $this->updateCachedMount($mount); } } /** * @param ICachedMountInfo[] $newMounts * @param ICachedMountInfo[] $cachedMounts * @return ICachedMountInfo[] */ private function findChangedMounts(array $newMounts, array $cachedMounts) { $changed = []; foreach ($newMounts as $newMount) { foreach ($cachedMounts as $cachedMount) { if ( $newMount->getRootId() === $cachedMount->getRootId() && ( $newMount->getMountPoint() !== $cachedMount->getMountPoint() || $newMount->getStorageId() !== $cachedMount->getStorageId() || $newMount->getMountId() !== $cachedMount->getMountId() ) ) { $changed[] = $newMount; } } } return $changed; } private function addToCache(ICachedMountInfo $mount) { if ($mount->getStorageId() !== -1) { $this->connection->insertIfNotExist('*PREFIX*mounts', [ 'storage_id' => $mount->getStorageId(), 'root_id' => $mount->getRootId(), 'user_id' => $mount->getUser()->getUID(), 'mount_point' => $mount->getMountPoint(), 'mount_id' => $mount->getMountId() ], ['root_id', 'user_id']); } else { // in some cases this is legitimate, like orphaned shares $this->logger->debug('Could not get storage info for mount at ' . $mount->getMountPoint()); } } private function updateCachedMount(ICachedMountInfo $mount) { $builder = $this->connection->getQueryBuilder(); $query = $builder->update('mounts') ->set('storage_id', $builder->createNamedParameter($mount->getStorageId())) ->set('mount_point', $builder->createNamedParameter($mount->getMountPoint())) ->set('mount_id', $builder->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT)) ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID()))) ->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT))); $query->execute(); } private function removeFromCache(ICachedMountInfo $mount) { $builder = $this->connection->getQueryBuilder(); $query = $builder->delete('mounts') ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID()))) ->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT))); $query->execute(); } private function dbRowToMountInfo(array $row) { $user = $this->userManager->get($row['user_id']); if (is_null($user)) { return null; } return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point'], $row['mount_id'], isset($row['path'])? $row['path']:''); } /** * @param IUser $user * @return ICachedMountInfo[] */ public function getMountsForUser(IUser $user) { if (!isset($this->mountsForUsers[$user->getUID()])) { $builder = $this->connection->getQueryBuilder(); $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path') ->from('mounts', 'm') ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid')) ->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID()))); $rows = $query->execute()->fetchAll(); $this->mountsForUsers[$user->getUID()] = array_filter(array_map([$this, 'dbRowToMountInfo'], $rows)); } return $this->mountsForUsers[$user->getUID()]; } /** * @param int $numericStorageId * @param string|null $user limit the results to a single user * @return CachedMountInfo[] */ public function getMountsForStorageId($numericStorageId, $user = null) { $builder = $this->connection->getQueryBuilder(); $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path') ->from('mounts', 'm') ->innerJoin('m', 'filecache', 'f' , $builder->expr()->eq('m.root_id', 'f.fileid')) ->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, IQueryBuilder::PARAM_INT))); if ($user) { $query->andWhere($builder->expr()->eq('user_id', $builder->createPositionalParameter($user))); } $rows = $query->execute()->fetchAll(); return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows)); } /** * @param int $rootFileId * @return CachedMountInfo[] */ public function getMountsForRootId($rootFileId) { $builder = $this->connection->getQueryBuilder(); $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path') ->from('mounts', 'm') ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid')) ->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, IQueryBuilder::PARAM_INT))); $rows = $query->execute()->fetchAll(); return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows)); } /** * @param $fileId * @return array * @throws \OCP\Files\NotFoundException */ private function getCacheInfoFromFileId($fileId) { if (!isset($this->cacheInfoCache[$fileId])) { $builder = $this->connection->getQueryBuilder(); $query = $builder->select('storage', 'path', 'mimetype') ->from('filecache') ->where($builder->expr()->eq('fileid', $builder->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); $row = $query->execute()->fetch(); if (is_array($row)) { $this->cacheInfoCache[$fileId] = [ (int)$row['storage'], $row['path'], (int)$row['mimetype'] ]; } else { throw new NotFoundException('File with id "' . $fileId . '" not found'); } } return $this->cacheInfoCache[$fileId]; } /** * @param int $fileId * @param string|null $user optionally restrict the results to a single user * @return ICachedMountInfo[] * @since 9.0.0 */ public function getMountsForFileId($fileId, $user = null) { try { list($storageId, $internalPath) = $this->getCacheInfoFromFileId($fileId); } catch (NotFoundException $e) { return []; } $mountsForStorage = $this->getMountsForStorageId($storageId, $user); // filter mounts that are from the same storage but a different directory return array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) { if ($fileId === $mount->getRootId()) { return true; } $internalMountPath = $mount->getRootInternalPath(); return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/'; }); } /** * Remove all cached mounts for a user * * @param IUser $user */ public function removeUserMounts(IUser $user) { $builder = $this->connection->getQueryBuilder(); $query = $builder->delete('mounts') ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($user->getUID()))); $query->execute(); } public function removeUserStorageMount($storageId, $userId) { $builder = $this->connection->getQueryBuilder(); $query = $builder->delete('mounts') ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($userId))) ->andWhere($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))); $query->execute(); } public function remoteStorageMounts($storageId) { $builder = $this->connection->getQueryBuilder(); $query = $builder->delete('mounts') ->where($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))); $query->execute(); } } private/Files/Config/UserMountCacheListener.php 0000604 00000002502 15247130452 0015567 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Config; use OC\User\Manager; use OCP\Files\Config\IUserMountCache; /** * Listen to hooks and update the mount cache as needed */ class UserMountCacheListener { /** * @var IUserMountCache */ private $userMountCache; /** * UserMountCacheListener constructor. * * @param IUserMountCache $userMountCache */ public function __construct(IUserMountCache $userMountCache) { $this->userMountCache = $userMountCache; } public function listen(Manager $manager) { $manager->listen('\OC\User', 'postDelete', [$this->userMountCache, 'removeUserMounts']); } } private/Files/Config/CachedMountInfo.php 0000604 00000005660 15247130452 0014212 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Config; use OC\Files\Filesystem; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Node; use OCP\IUser; class CachedMountInfo implements ICachedMountInfo { /** * @var IUser */ protected $user; /** * @var int */ protected $storageId; /** * @var int */ protected $rootId; /** * @var string */ protected $mountPoint; /** * @var int|null */ protected $mountId; /** * @var string */ protected $rootInternalPath; /** * CachedMountInfo constructor. * * @param IUser $user * @param int $storageId * @param int $rootId * @param string $mountPoint * @param int|null $mountId * @param string $rootInternalPath */ public function __construct(IUser $user, $storageId, $rootId, $mountPoint, $mountId = null, $rootInternalPath = '') { $this->user = $user; $this->storageId = $storageId; $this->rootId = $rootId; $this->mountPoint = $mountPoint; $this->mountId = $mountId; $this->rootInternalPath = $rootInternalPath; } /** * @return IUser */ public function getUser() { return $this->user; } /** * @return int the numeric storage id of the mount */ public function getStorageId() { return $this->storageId; } /** * @return int the fileid of the root of the mount */ public function getRootId() { return $this->rootId; } /** * @return Node the root node of the mount */ public function getMountPointNode() { // TODO injection etc Filesystem::initMountPoints($this->getUser()->getUID()); $userNode = \OC::$server->getUserFolder($this->getUser()->getUID()); $nodes = $userNode->getParent()->getById($this->getRootId()); if (count($nodes) > 0) { return $nodes[0]; } else { return null; } } /** * @return string the mount point of the mount for the user */ public function getMountPoint() { return $this->mountPoint; } /** * Get the id of the configured mount * * @return int|null mount id or null if not applicable * @since 9.1.0 */ public function getMountId() { return $this->mountId; } /** * Get the internal path (within the storage) of the root of the mount * * @return string */ public function getRootInternalPath() { return $this->rootInternalPath; } } private/Files/Config/LazyStorageMountInfo.php 0000604 00000004062 15247130452 0015302 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Config; use OCP\Files\Mount\IMountPoint; use OCP\IUser; class LazyStorageMountInfo extends CachedMountInfo { /** @var IMountPoint */ private $mount; /** * CachedMountInfo constructor. * * @param IUser $user * @param IMountPoint $mount */ public function __construct(IUser $user, IMountPoint $mount) { $this->user = $user; $this->mount = $mount; } /** * @return int the numeric storage id of the mount */ public function getStorageId() { if (!$this->storageId) { $this->storageId = $this->mount->getNumericStorageId(); } return parent::getStorageId(); } /** * @return int the fileid of the root of the mount */ public function getRootId() { if (!$this->rootId) { $this->rootId = $this->mount->getStorageRootId(); } return parent::getRootId(); } /** * @return string the mount point of the mount for the user */ public function getMountPoint() { if (!$this->mountPoint) { $this->mountPoint = $this->mount->getMountPoint(); } return parent::getMountPoint(); } public function getMountId() { return $this->mount->getMountId(); } /** * Get the internal path (within the storage) of the root of the mount * * @return string */ public function getRootInternalPath() { return $this->mount->getInternalPath($this->mount->getMountPoint()); } } private/Files/Search/SearchQuery.php 0000604 00000004112 15247130452 0013426 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\Search; use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchOrder; use OCP\Files\Search\ISearchQuery; use OCP\IUser; class SearchQuery implements ISearchQuery { /** @var ISearchOperator */ private $searchOperation; /** @var integer */ private $limit; /** @var integer */ private $offset; /** @var ISearchOrder[] */ private $order; /** @var IUser */ private $user; /** * SearchQuery constructor. * * @param ISearchOperator $searchOperation * @param int $limit * @param int $offset * @param array $order * @param IUser $user */ public function __construct(ISearchOperator $searchOperation, $limit, $offset, array $order, IUser $user) { $this->searchOperation = $searchOperation; $this->limit = $limit; $this->offset = $offset; $this->order = $order; $this->user = $user; } /** * @return ISearchOperator */ public function getSearchOperation() { return $this->searchOperation; } /** * @return int */ public function getLimit() { return $this->limit; } /** * @return int */ public function getOffset() { return $this->offset; } /** * @return ISearchOrder[] */ public function getOrder() { return $this->order; } /** * @return IUser */ public function getUser() { return $this->user; } } private/Files/Search/SearchOrder.php 0000604 00000002557 15247130452 0013407 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\Search; use OCP\Files\Search\ISearchOrder; class SearchOrder implements ISearchOrder { /** @var string */ private $direction; /** @var string */ private $field; /** * SearchOrder constructor. * * @param string $direction * @param string $field */ public function __construct($direction, $field) { $this->direction = $direction; $this->field = $field; } /** * @return string */ public function getDirection() { return $this->direction; } /** * @return string */ public function getField() { return $this->field; } } private/Files/Search/SearchBinaryOperator.php 0000604 00000002726 15247130452 0015272 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\Search; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchOperator; class SearchBinaryOperator implements ISearchBinaryOperator { /** @var string */ private $type; /** @var ISearchOperator[] */ private $arguments; /** * SearchBinaryOperator constructor. * * @param string $type * @param ISearchOperator[] $arguments */ public function __construct($type, array $arguments) { $this->type = $type; $this->arguments = $arguments; } /** * @return string */ public function getType() { return $this->type; } /** * @return ISearchOperator[] */ public function getArguments() { return $this->arguments; } } private/Files/Search/SearchComparison.php 0000604 00000003100 15247130452 0014427 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\Search; use OCP\Files\Search\ISearchComparison; class SearchComparison implements ISearchComparison { /** @var string */ private $type; /** @var string */ private $field; /** @var string|integer|\DateTime */ private $value; /** * SearchComparison constructor. * * @param string $type * @param string $field * @param \DateTime|int|string $value */ public function __construct($type, $field, $value) { $this->type = $type; $this->field = $field; $this->value = $value; } /** * @return string */ public function getType() { return $this->type; } /** * @return string */ public function getField() { return $this->field; } /** * @return \DateTime|int|string */ public function getValue() { return $this->value; } } private/Files/Utils/Scanner.php 0000604 00000020577 15247130452 0012474 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Utils; use OC\Files\Cache\Cache; use OC\Files\Filesystem; use OC\ForbiddenException; use OC\Hooks\PublicEmitter; use OC\Lock\DBLockingProvider; use OCA\Files_Sharing\SharedStorage; use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorage; use OCP\Files\StorageNotAvailableException; use OCP\ILogger; /** * Class Scanner * * Hooks available in scope \OC\Utils\Scanner * - scanFile(string $absolutePath) * - scanFolder(string $absolutePath) * * @package OC\Files\Utils */ class Scanner extends PublicEmitter { const MAX_ENTRIES_TO_COMMIT = 10000; /** * @var string $user */ private $user; /** * @var \OCP\IDBConnection */ protected $db; /** * @var ILogger */ protected $logger; /** * Whether to use a DB transaction * * @var bool */ protected $useTransaction; /** * Number of entries scanned to commit * * @var int */ protected $entriesToCommit; /** * @param string $user * @param \OCP\IDBConnection $db * @param ILogger $logger */ public function __construct($user, $db, ILogger $logger) { $this->logger = $logger; $this->user = $user; $this->db = $db; // when DB locking is used, no DB transactions will be used $this->useTransaction = !(\OC::$server->getLockingProvider() instanceof DBLockingProvider); } /** * get all storages for $dir * * @param string $dir * @return \OC\Files\Mount\MountPoint[] */ protected function getMounts($dir) { //TODO: move to the node based fileapi once that's done \OC_Util::tearDownFS(); \OC_Util::setupFS($this->user); $mountManager = Filesystem::getMountManager(); $mounts = $mountManager->findIn($dir); $mounts[] = $mountManager->find($dir); $mounts = array_reverse($mounts); //start with the mount of $dir return $mounts; } /** * attach listeners to the scanner * * @param \OC\Files\Mount\MountPoint $mount */ protected function attachListener($mount) { $scanner = $mount->getStorage()->getScanner(); $emitter = $this; $scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function ($path) use ($mount, $emitter) { $emitter->emit('\OC\Files\Utils\Scanner', 'scanFile', array($mount->getMountPoint() . $path)); }); $scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function ($path) use ($mount, $emitter) { $emitter->emit('\OC\Files\Utils\Scanner', 'scanFolder', array($mount->getMountPoint() . $path)); }); $scanner->listen('\OC\Files\Cache\Scanner', 'postScanFile', function ($path) use ($mount, $emitter) { $emitter->emit('\OC\Files\Utils\Scanner', 'postScanFile', array($mount->getMountPoint() . $path)); }); $scanner->listen('\OC\Files\Cache\Scanner', 'postScanFolder', function ($path) use ($mount, $emitter) { $emitter->emit('\OC\Files\Utils\Scanner', 'postScanFolder', array($mount->getMountPoint() . $path)); }); } /** * @param string $dir */ public function backgroundScan($dir) { $mounts = $this->getMounts($dir); foreach ($mounts as $mount) { $storage = $mount->getStorage(); if (is_null($storage)) { continue; } // don't bother scanning failed storages (shortcut for same result) if ($storage->instanceOfStorage('OC\Files\Storage\FailedStorage')) { continue; } // don't scan the root storage if ($storage->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') { continue; } // don't scan received local shares, these can be scanned when scanning the owner's storage if ($storage->instanceOfStorage(SharedStorage::class)) { continue; } $scanner = $storage->getScanner(); $this->attachListener($mount); $scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', function ($path) use ($storage) { $this->triggerPropagator($storage, $path); }); $scanner->listen('\OC\Files\Cache\Scanner', 'updateCache', function ($path) use ($storage) { $this->triggerPropagator($storage, $path); }); $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function ($path) use ($storage) { $this->triggerPropagator($storage, $path); }); $propagator = $storage->getPropagator(); $propagator->beginBatch(); $scanner->backgroundScan(); $propagator->commitBatch(); } } /** * @param string $dir * @throws \OC\ForbiddenException * @throws \OCP\Files\NotFoundException */ public function scan($dir = '') { if (!Filesystem::isValidPath($dir)) { throw new \InvalidArgumentException('Invalid path to scan'); } $mounts = $this->getMounts($dir); foreach ($mounts as $mount) { $storage = $mount->getStorage(); if (is_null($storage)) { continue; } // don't bother scanning failed storages (shortcut for same result) if ($storage->instanceOfStorage('OC\Files\Storage\FailedStorage')) { continue; } // if the home storage isn't writable then the scanner is run as the wrong user if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and (!$storage->isCreatable('') or !$storage->isCreatable('files')) ) { if ($storage->file_exists('') or $storage->getCache()->inCache('')) { throw new ForbiddenException(); } else {// if the root exists in neither the cache nor the storage the user isn't setup yet break; } } // don't scan received local shares, these can be scanned when scanning the owner's storage if ($storage->instanceOfStorage(SharedStorage::class)) { continue; } $relativePath = $mount->getInternalPath($dir); $scanner = $storage->getScanner(); $scanner->setUseTransactions(false); $this->attachListener($mount); $scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', function ($path) use ($storage) { $this->postProcessEntry($storage, $path); }); $scanner->listen('\OC\Files\Cache\Scanner', 'updateCache', function ($path) use ($storage) { $this->postProcessEntry($storage, $path); }); $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function ($path) use ($storage) { $this->postProcessEntry($storage, $path); }); if (!$storage->file_exists($relativePath)) { throw new NotFoundException($dir); } if ($this->useTransaction) { $this->db->beginTransaction(); } try { $propagator = $storage->getPropagator(); $propagator->beginBatch(); $scanner->scan($relativePath, \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE); $cache = $storage->getCache(); if ($cache instanceof Cache) { // only re-calculate for the root folder we scanned, anything below that is taken care of by the scanner $cache->correctFolderSize($relativePath); } $propagator->commitBatch(); } catch (StorageNotAvailableException $e) { $this->logger->error('Storage ' . $storage->getId() . ' not available'); $this->logger->logException($e); $this->emit('\OC\Files\Utils\Scanner', 'StorageNotAvailable', [$e]); } if ($this->useTransaction) { $this->db->commit(); } } } private function triggerPropagator(IStorage $storage, $internalPath) { $storage->getPropagator()->propagateChange($internalPath, time()); } private function postProcessEntry(IStorage $storage, $internalPath) { $this->triggerPropagator($storage, $internalPath); if ($this->useTransaction) { $this->entriesToCommit++; if ($this->entriesToCommit >= self::MAX_ENTRIES_TO_COMMIT) { $propagator = $storage->getPropagator(); $this->entriesToCommit = 0; $this->db->commit(); $propagator->commitBatch(); $this->db->beginTransaction(); $propagator->beginBatch(); } } } } private/Files/FileInfo.php 0000604 00000020745 15247130452 0011473 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author tbartenstein <tbartenstein@users.noreply.github.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Mount\IMountPoint; use OCP\IUser; class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { /** * @var array $data */ private $data; /** * @var string $path */ private $path; /** * @var \OC\Files\Storage\Storage $storage */ private $storage; /** * @var string $internalPath */ private $internalPath; /** * @var \OCP\Files\Mount\IMountPoint */ private $mount; /** * @var IUser */ private $owner; /** * @var string[] */ private $childEtags = []; /** * @var IMountPoint[] */ private $subMounts = []; private $subMountsUsed = false; /** * @param string|boolean $path * @param Storage\Storage $storage * @param string $internalPath * @param array|ICacheEntry $data * @param \OCP\Files\Mount\IMountPoint $mount * @param \OCP\IUser|null $owner */ public function __construct($path, $storage, $internalPath, $data, $mount, $owner= null) { $this->path = $path; $this->storage = $storage; $this->internalPath = $internalPath; $this->data = $data; $this->mount = $mount; $this->owner = $owner; } public function offsetSet($offset, $value) { $this->data[$offset] = $value; } public function offsetExists($offset) { return isset($this->data[$offset]); } public function offsetUnset($offset) { unset($this->data[$offset]); } public function offsetGet($offset) { if ($offset === 'type') { return $this->getType(); } else if ($offset === 'etag') { return $this->getEtag(); } else if ($offset === 'size') { return $this->getSize(); } else if ($offset === 'mtime') { return $this->getMTime(); } elseif ($offset === 'permissions') { return $this->getPermissions(); } elseif (isset($this->data[$offset])) { return $this->data[$offset]; } else { return null; } } /** * @return string */ public function getPath() { return $this->path; } /** * @return \OCP\Files\Storage */ public function getStorage() { return $this->storage; } /** * @return string */ public function getInternalPath() { return $this->internalPath; } /** * Get FileInfo ID or null in case of part file * * @return int|null */ public function getId() { return isset($this->data['fileid']) ? (int) $this->data['fileid'] : null; } /** * @return string */ public function getMimetype() { return $this->data['mimetype']; } /** * @return string */ public function getMimePart() { return $this->data['mimepart']; } /** * @return string */ public function getName() { return basename($this->getPath()); } /** * @return string */ public function getEtag() { $this->updateEntryfromSubMounts(); if (count($this->childEtags) > 0) { $combinedEtag = $this->data['etag'] . '::' . implode('::', $this->childEtags); return md5($combinedEtag); } else { return $this->data['etag']; } } /** * @return int */ public function getSize() { $this->updateEntryfromSubMounts(); return isset($this->data['size']) ? 0 + $this->data['size'] : 0; } /** * @return int */ public function getMTime() { $this->updateEntryfromSubMounts(); return (int) $this->data['mtime']; } /** * @return bool */ public function isEncrypted() { return $this->data['encrypted']; } /** * Return the currently version used for the HMAC in the encryption app * * @return int */ public function getEncryptedVersion() { return isset($this->data['encryptedVersion']) ? (int) $this->data['encryptedVersion'] : 1; } /** * @return int */ public function getPermissions() { $perms = (int) $this->data['permissions']; if (\OCP\Util::isSharingDisabledForUser() || ($this->isShared() && !\OC\Share\Share::isResharingAllowed())) { $perms = $perms & ~\OCP\Constants::PERMISSION_SHARE; } return (int) $perms; } /** * @return \OCP\Files\FileInfo::TYPE_FILE|\OCP\Files\FileInfo::TYPE_FOLDER */ public function getType() { if (!isset($this->data['type'])) { $this->data['type'] = ($this->getMimetype() === 'httpd/unix-directory') ? self::TYPE_FOLDER : self::TYPE_FILE; } return $this->data['type']; } public function getData() { return $this->data; } /** * @param int $permissions * @return bool */ protected function checkPermissions($permissions) { return ($this->getPermissions() & $permissions) === $permissions; } /** * @return bool */ public function isReadable() { return $this->checkPermissions(\OCP\Constants::PERMISSION_READ); } /** * @return bool */ public function isUpdateable() { return $this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE); } /** * Check whether new files or folders can be created inside this folder * * @return bool */ public function isCreatable() { return $this->checkPermissions(\OCP\Constants::PERMISSION_CREATE); } /** * @return bool */ public function isDeletable() { return $this->checkPermissions(\OCP\Constants::PERMISSION_DELETE); } /** * @return bool */ public function isShareable() { return $this->checkPermissions(\OCP\Constants::PERMISSION_SHARE); } /** * Check if a file or folder is shared * * @return bool */ public function isShared() { $sid = $this->getStorage()->getId(); if (!is_null($sid)) { $sid = explode(':', $sid); return ($sid[0] === 'shared'); } return false; } public function isMounted() { $storage = $this->getStorage(); if ($storage->instanceOfStorage('\OCP\Files\IHomeStorage')) { return false; } $sid = $storage->getId(); if (!is_null($sid)) { $sid = explode(':', $sid); return ($sid[0] !== 'home' and $sid[0] !== 'shared'); } return false; } /** * Get the mountpoint the file belongs to * * @return \OCP\Files\Mount\IMountPoint */ public function getMountPoint() { return $this->mount; } /** * Get the owner of the file * * @return \OCP\IUser */ public function getOwner() { return $this->owner; } /** * @param IMountPoint[] $mounts */ public function setSubMounts(array $mounts) { $this->subMounts = $mounts; } private function updateEntryfromSubMounts() { if ($this->subMountsUsed) { return; } $this->subMountsUsed = true; foreach ($this->subMounts as $mount) { $subStorage = $mount->getStorage(); if ($subStorage) { $subCache = $subStorage->getCache(''); $rootEntry = $subCache->get(''); $this->addSubEntry($rootEntry, $mount->getMountPoint()); } } } /** * Add a cache entry which is the child of this folder * * Sets the size, etag and size to for cross-storage childs * * @param array|ICacheEntry $data cache entry for the child * @param string $entryPath full path of the child entry */ public function addSubEntry($data, $entryPath) { $this->data['size'] += isset($data['size']) ? $data['size'] : 0; if (isset($data['mtime'])) { $this->data['mtime'] = max($this->data['mtime'], $data['mtime']); } if (isset($data['etag'])) { // prefix the etag with the relative path of the subentry to propagate etag on mount moves $relativeEntryPath = substr($entryPath, strlen($this->getPath())); // attach the permissions to propagate etag on permision changes of submounts $permissions = isset($data['permissions']) ? $data['permissions'] : 0; $this->childEtags[] = $relativeEntryPath . '/' . $data['etag'] . $permissions; } } /** * @inheritdoc */ public function getChecksum() { return $this->data['checksum']; } } private/Files/ObjectStore/Mapper.php 0000604 00000002430 15247130452 0013436 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\ObjectStore; use OCP\IUser; /** * Class Mapper * * @package OC\Files\ObjectStore * * Map a user to a bucket. */ class Mapper { /** @var IUser */ private $user; /** * Mapper constructor. * * @param IUser $user */ public function __construct(IUser $user) { $this->user = $user; } /** * @param int $numBuckets * @return string */ public function getBucket($numBuckets = 64) { $hash = md5($this->user->getUID()); $num = hexdec(substr($hash, 0, 4)); return (string)($num % $numBuckets); } } private/Files/ObjectStore/S3.php 0000604 00000005532 15247130452 0012505 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\ObjectStore; use OCP\Files\ObjectStore\IObjectStore; // TODO: proper composer set_include_path(get_include_path() . PATH_SEPARATOR . \OC_App::getAppPath('files_external') . '/3rdparty/aws-sdk-php'); require_once 'aws-autoloader.php'; class S3 implements IObjectStore { use S3ConnectionTrait; public function __construct($parameters) { $this->parseParams($parameters); } /** * @return string the container or bucket name where objects are stored * @since 7.0.0 */ function getStorageId() { return $this->id; } /** * @param string $urn the unified resource name used to identify the object * @return resource stream with the read data * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function readObject($urn) { // Create the command and serialize the request $request = $this->getConnection()->getCommand('GetObject', [ 'Bucket' => $this->bucket, 'Key' => $urn ])->prepare(); $request->dispatch('request.before_send', array( 'request' => $request )); $headers = $request->getHeaderLines(); $headers[] = 'Connection: close'; $opts = [ 'http' => [ 'method' => "GET", 'header' => $headers ], 'ssl' => [ 'verify_peer' => true ] ]; $context = stream_context_create($opts); return fopen($request->getUrl(), 'r', false, $context); } /** * @param string $urn the unified resource name used to identify the object * @param resource $stream stream with the data to write * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function writeObject($urn, $stream) { $this->getConnection()->putObject([ 'Bucket' => $this->bucket, 'Key' => $urn, 'Body' => $stream ]); } /** * @param string $urn the unified resource name used to identify the object * @return void * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function deleteObject($urn) { $this->getConnection()->deleteObject([ 'Bucket' => $this->bucket, 'Key' => $urn ]); } } private/Files/ObjectStore/ObjectStoreStorage.php 0000604 00000025455 15247130452 0015776 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\ObjectStore; use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use OC\Files\Cache\CacheEntry; use OCP\Files\ObjectStore\IObjectStore; class ObjectStoreStorage extends \OC\Files\Storage\Common { /** * @var \OCP\Files\ObjectStore\IObjectStore $objectStore */ protected $objectStore; /** * @var string $id */ protected $id; /** * @var \OC\User\User $user */ protected $user; private $objectPrefix = 'urn:oid:'; private $logger; public function __construct($params) { if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) { $this->objectStore = $params['objectstore']; } else { throw new \Exception('missing IObjectStore instance'); } if (isset($params['storageid'])) { $this->id = 'object::store:' . $params['storageid']; } else { $this->id = 'object::store:' . $this->objectStore->getStorageId(); } if (isset($params['objectPrefix'])) { $this->objectPrefix = $params['objectPrefix']; } //initialize cache with root directory in cache if (!$this->is_dir('/')) { $this->mkdir('/'); } $this->logger = \OC::$server->getLogger(); } public function mkdir($path) { $path = $this->normalizePath($path); if ($this->file_exists($path)) { return false; } $mTime = time(); $data = [ 'mimetype' => 'httpd/unix-directory', 'size' => 0, 'mtime' => $mTime, 'storage_mtime' => $mTime, 'permissions' => \OCP\Constants::PERMISSION_ALL, ]; if ($path === '') { //create root on the fly $data['etag'] = $this->getETag(''); $this->getCache()->put('', $data); return true; } else { // if parent does not exist, create it $parent = $this->normalizePath(dirname($path)); $parentType = $this->filetype($parent); if ($parentType === false) { if (!$this->mkdir($parent)) { // something went wrong return false; } } else if ($parentType === 'file') { // parent is a file return false; } // finally create the new dir $mTime = time(); // update mtime $data['mtime'] = $mTime; $data['storage_mtime'] = $mTime; $data['etag'] = $this->getETag($path); $this->getCache()->put($path, $data); return true; } } /** * @param string $path * @return string */ private function normalizePath($path) { $path = trim($path, '/'); //FIXME why do we sometimes get a path like 'files//username'? $path = str_replace('//', '/', $path); // dirname('/folder') returns '.' but internally (in the cache) we store the root as '' if (!$path || $path === '.') { $path = ''; } return $path; } /** * Object Stores use a NoopScanner because metadata is directly stored in * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere. * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner * @return \OC\Files\ObjectStore\NoopScanner */ public function getScanner($path = '', $storage = null) { if (!$storage) { $storage = $this; } if (!isset($this->scanner)) { $this->scanner = new NoopScanner($storage); } return $this->scanner; } public function getId() { return $this->id; } public function rmdir($path) { $path = $this->normalizePath($path); if (!$this->is_dir($path)) { return false; } $this->rmObjects($path); $this->getCache()->remove($path); return true; } private function rmObjects($path) { $children = $this->getCache()->getFolderContents($path); foreach ($children as $child) { if ($child['mimetype'] === 'httpd/unix-directory') { $this->rmObjects($child['path']); } else { $this->unlink($child['path']); } } } public function unlink($path) { $path = $this->normalizePath($path); $stat = $this->stat($path); if ($stat && isset($stat['fileid'])) { if ($stat['mimetype'] === 'httpd/unix-directory') { return $this->rmdir($path); } try { $this->objectStore->deleteObject($this->getURN($stat['fileid'])); } catch (\Exception $ex) { if ($ex->getCode() !== 404) { $this->logger->logException($ex, [ 'app' => 'objectstore', 'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path, ]); return false; } else { //removing from cache is ok as it does not exist in the objectstore anyway } } $this->getCache()->remove($path); return true; } return false; } public function stat($path) { $path = $this->normalizePath($path); $cacheEntry = $this->getCache()->get($path); if ($cacheEntry instanceof CacheEntry) { return $cacheEntry->getData(); } else { return false; } } /** * Override this method if you need a different unique resource identifier for your object storage implementation. * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users. * You may need a mapping table to store your URN if it cannot be generated from the fileid. * * @param int $fileId the fileid * @return null|string the unified resource name used to identify the object */ protected function getURN($fileId) { if (is_numeric($fileId)) { return $this->objectPrefix . $fileId; } return null; } public function opendir($path) { $path = $this->normalizePath($path); try { $files = array(); $folderContents = $this->getCache()->getFolderContents($path); foreach ($folderContents as $file) { $files[] = $file['name']; } return IteratorDirectory::wrap($files); } catch (\Exception $e) { $this->logger->logException($e); return false; } } public function filetype($path) { $path = $this->normalizePath($path); $stat = $this->stat($path); if ($stat) { if ($stat['mimetype'] === 'httpd/unix-directory') { return 'dir'; } return 'file'; } else { return false; } } public function fopen($path, $mode) { $path = $this->normalizePath($path); switch ($mode) { case 'r': case 'rb': $stat = $this->stat($path); if (is_array($stat)) { try { return $this->objectStore->readObject($this->getURN($stat['fileid'])); } catch (\Exception $ex) { $this->logger->logException($ex, [ 'app' => 'objectstore', 'message' => 'Count not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path, ]); return false; } } else { return false; } case 'w': case 'wb': case 'a': case 'ab': case 'r+': case 'w+': case 'wb+': case 'a+': case 'x': case 'x+': case 'c': case 'c+': if (strrpos($path, '.') !== false) { $ext = substr($path, strrpos($path, '.')); } else { $ext = ''; } $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); } $handle = fopen($tmpFile, $mode); return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { $this->writeBack($tmpFile, $path); }); } return false; } public function file_exists($path) { $path = $this->normalizePath($path); return (bool)$this->stat($path); } public function rename($source, $target) { $source = $this->normalizePath($source); $target = $this->normalizePath($target); $this->remove($target); $this->getCache()->move($source, $target); $this->touch(dirname($target)); return true; } public function getMimeType($path) { $path = $this->normalizePath($path); $stat = $this->stat($path); if (is_array($stat)) { return $stat['mimetype']; } else { return false; } } public function touch($path, $mtime = null) { if (is_null($mtime)) { $mtime = time(); } $path = $this->normalizePath($path); $dirName = dirname($path); $parentExists = $this->is_dir($dirName); if (!$parentExists) { return false; } $stat = $this->stat($path); if (is_array($stat)) { // update existing mtime in db $stat['mtime'] = $mtime; $this->getCache()->update($stat['fileid'], $stat); } else { $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path); // create new file $stat = array( 'etag' => $this->getETag($path), 'mimetype' => $mimeType, 'size' => 0, 'mtime' => $mtime, 'storage_mtime' => $mtime, 'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, ); $fileId = $this->getCache()->put($path, $stat); try { //read an empty file from memory $this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r')); } catch (\Exception $ex) { $this->getCache()->remove($path); $this->logger->logException($ex, [ 'app' => 'objectstore', 'message' => 'Could not create object ' . $this->getURN($fileId) . ' for ' . $path, ]); return false; } } return true; } public function writeBack($tmpFile, $path) { $stat = $this->stat($path); if (empty($stat)) { // create new file $stat = array( 'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, ); } // update stat with new data $mTime = time(); $stat['size'] = filesize($tmpFile); $stat['mtime'] = $mTime; $stat['storage_mtime'] = $mTime; $stat['mimetype'] = \OC::$server->getMimeTypeDetector()->detect($tmpFile); $stat['etag'] = $this->getETag($path); $fileId = $this->getCache()->put($path, $stat); try { //upload to object storage $this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r')); } catch (\Exception $ex) { $this->getCache()->remove($path); $this->logger->logException($ex, [ 'app' => 'objectstore', 'message' => 'Could not create object ' . $this->getURN($fileId) . ' for ' . $path, ]); throw $ex; // make this bubble up } } /** * external changes are not supported, exclusive access to the object storage is assumed * * @param string $path * @param int $time * @return false */ public function hasUpdated($path, $time) { return false; } } private/Files/ObjectStore/HomeObjectStoreStorage.php 0000604 00000003444 15247130452 0016601 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\ObjectStore; use OC\User\User; class HomeObjectStoreStorage extends ObjectStoreStorage implements \OCP\Files\IHomeStorage { /** * The home user storage requires a user object to create a unique storage id * @param array $params */ public function __construct($params) { if ( ! isset($params['user']) || ! $params['user'] instanceof User) { throw new \Exception('missing user object in parameters'); } $this->user = $params['user']; parent::__construct($params); } public function getId () { return 'object::user:' . $this->user->getUID(); } /** * get the owner of a path * * @param string $path The path to get the owner * @return false|string uid */ public function getOwner($path) { if (is_object($this->user)) { return $this->user->getUID(); } return false; } /** * @param string $path, optional * @return \OC\User\User */ public function getUser($path = null) { return $this->user; } } private/Files/ObjectStore/StorageObjectStore.php 0000604 00000004636 15247130452 0015774 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\ObjectStore; use OCP\Files\ObjectStore\IObjectStore; use OCP\Files\Storage\IStorage; /** * Object store that wraps a storage backend, mostly for testing purposes */ class StorageObjectStore implements IObjectStore { /** @var IStorage */ private $storage; /** * @param IStorage $storage */ public function __construct(IStorage $storage) { $this->storage = $storage; } /** * @return string the container or bucket name where objects are stored * @since 7.0.0 */ function getStorageId() { $this->storage->getId(); } /** * @param string $urn the unified resource name used to identify the object * @return resource stream with the read data * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function readObject($urn) { $handle = $this->storage->fopen($urn, 'r'); if ($handle) { return $handle; } else { throw new \Exception(); } } /** * @param string $urn the unified resource name used to identify the object * @param resource $stream stream with the data to write * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function writeObject($urn, $stream) { $handle = $this->storage->fopen($urn, 'w'); if ($handle) { stream_copy_to_stream($stream, $handle); fclose($handle); } else { throw new \Exception(); } } /** * @param string $urn the unified resource name used to identify the object * @return void * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ function deleteObject($urn) { $this->storage->unlink($urn); } } private/Files/ObjectStore/NoopScanner.php 0000604 00000004572 15247130452 0014450 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\ObjectStore; use \OC\Files\Cache\Scanner; use \OC\Files\Storage\Storage; class NoopScanner extends Scanner { public function __construct(Storage $storage) { //we don't need the storage, so do nothing here } /** * scan a single file and store it in the cache * * @param string $file * @param int $reuseExisting * @param int $parentId * @param array|null $cacheData existing data in the cache for the file to be scanned * @return array an array of metadata of the scanned file */ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) { return array(); } /** * scan a folder and all it's children * * @param string $path * @param bool $recursive * @param int $reuse * @return array with the meta data of the scanned file or folder */ public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { return array(); } /** * scan all the files and folders in a folder * * @param string $path * @param bool $recursive * @param int $reuse * @param array $folderData existing cache data for the folder to be scanned * @return int the size of the scanned folder or -1 if the size is unknown at this stage */ protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderData = null, $lock = true) { return 0; } /** * walk over any folders that are not fully scanned yet and scan them */ public function backgroundScan() { //noop } } private/Files/ObjectStore/Swift.php 0000604 00000020464 15247130452 0013315 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\ObjectStore; use Guzzle\Http\Exception\ClientErrorResponseException; use OCP\Files\ObjectStore\IObjectStore; use OCP\Files\StorageAuthException; use OCP\Files\StorageNotAvailableException; use OpenCloud\Common\Service\Catalog; use OpenCloud\Common\Service\CatalogItem; use OpenCloud\Identity\Resource\Token; use OpenCloud\ObjectStore\Service; use OpenCloud\OpenStack; use OpenCloud\Rackspace; class Swift implements IObjectStore { /** * @var \OpenCloud\OpenStack */ private $client; /** * @var array */ private $params; /** * @var \OpenCloud\ObjectStore\Service */ private $objectStoreService; /** * @var \OpenCloud\ObjectStore\Resource\Container */ private $container; private $memcache; public function __construct($params) { if (isset($params['bucket'])) { $params['container'] = $params['bucket']; } if (!isset($params['container'])) { $params['container'] = 'owncloud'; } if (!isset($params['autocreate'])) { // should only be true for tests $params['autocreate'] = false; } if (isset($params['apiKey'])) { $this->client = new Rackspace($params['url'], $params); $cacheKey = $this->params['username'] . '@' . $this->params['url'] . '/' . $this->params['bucket']; } else { $this->client = new OpenStack($params['url'], $params); $cacheKey = $this->params['username'] . '@' . $this->params['url'] . '/' . $this->params['bucket']; } $cacheFactory = \OC::$server->getMemCacheFactory(); $this->memcache = $cacheFactory->create('swift::' . $cacheKey); $this->params = $params; } protected function init() { if ($this->container) { return; } $this->importToken(); /** @var Token $token */ $token = $this->client->getTokenObject(); if (!$token || $token->hasExpired()) { try { $this->client->authenticate(); $this->exportToken(); } catch (ClientErrorResponseException $e) { $statusCode = $e->getResponse()->getStatusCode(); if ($statusCode == 412) { throw new StorageAuthException('Precondition failed, verify the keystone url', $e); } else if ($statusCode === 401) { throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e); } else { throw new StorageAuthException('Unknown error', $e); } } } /** @var Catalog $catalog */ $catalog = $this->client->getCatalog(); if (isset($this->params['serviceName'])) { $serviceName = $this->params['serviceName']; } else { $serviceName = Service::DEFAULT_NAME; } if (isset($this->params['urlType'])) { $urlType = $this->params['urlType']; if ($urlType !== 'internalURL' && $urlType !== 'publicURL') { throw new StorageNotAvailableException('Invalid url type'); } } else { $urlType = Service::DEFAULT_URL_TYPE; } $catalogItem = $this->getCatalogForService($catalog, $serviceName); if (!$catalogItem) { $available = implode(', ', $this->getAvailableServiceNames($catalog)); throw new StorageNotAvailableException( "Service $serviceName not found in service catalog, available services: $available" ); } else if (isset($this->params['region'])) { $this->validateRegion($catalogItem, $this->params['region']); } $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType); try { $this->container = $this->objectStoreService->getContainer($this->params['container']); } catch (ClientErrorResponseException $ex) { // if the container does not exist and autocreate is true try to create the container on the fly if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) { $this->container = $this->objectStoreService->createContainer($this->params['container']); } else { throw $ex; } } } private function exportToken() { $export = $this->client->exportCredentials(); $export['catalog'] = array_map(function (CatalogItem $item) { return [ 'name' => $item->getName(), 'endpoints' => $item->getEndpoints(), 'type' => $item->getType() ]; }, $export['catalog']->getItems()); $this->memcache->set('token', json_encode($export)); } private function importToken() { $cachedTokenString = $this->memcache->get('token'); if ($cachedTokenString) { $cachedToken = json_decode($cachedTokenString, true); $cachedToken['catalog'] = array_map(function (array $item) { $itemClass = new \stdClass(); $itemClass->name = $item['name']; $itemClass->endpoints = array_map(function (array $endpoint) { return (object) $endpoint; }, $item['endpoints']); $itemClass->type = $item['type']; return $itemClass; }, $cachedToken['catalog']); try { $this->client->importCredentials($cachedToken); } catch (\Exception $e) { $this->client->setTokenObject(new Token()); } } } /** * @param Catalog $catalog * @param $name * @return null|CatalogItem */ private function getCatalogForService(Catalog $catalog, $name) { foreach ($catalog->getItems() as $item) { /** @var CatalogItem $item */ if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) { return $item; } } return null; } private function validateRegion(CatalogItem $item, $region) { $endPoints = $item->getEndpoints(); foreach ($endPoints as $endPoint) { if ($endPoint->region === $region) { return; } } $availableRegions = implode(', ', array_map(function ($endpoint) { return $endpoint->region; }, $endPoints)); throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions"); } private function getAvailableServiceNames(Catalog $catalog) { return array_map(function (CatalogItem $item) { return $item->getName(); }, array_filter($catalog->getItems(), function (CatalogItem $item) { return $item->hasType(Service::DEFAULT_TYPE); })); } /** * @return string the container name where objects are stored */ public function getStorageId() { return $this->params['container']; } /** * @param string $urn the unified resource name used to identify the object * @param resource $stream stream with the data to write * @throws Exception from openstack lib when something goes wrong */ public function writeObject($urn, $stream) { $this->init(); $this->container->uploadObject($urn, $stream); } /** * @param string $urn the unified resource name used to identify the object * @return resource stream with the read data * @throws Exception from openstack lib when something goes wrong */ public function readObject($urn) { $this->init(); $object = $this->container->getObject($urn); // we need to keep a reference to objectContent or // the stream will be closed before we can do anything with it /** @var $objectContent \Guzzle\Http\EntityBody * */ $objectContent = $object->getContent(); $objectContent->rewind(); $stream = $objectContent->getStream(); // save the object content in the context of the stream to prevent it being gc'd until the stream is closed stream_context_set_option($stream, 'swift', 'content', $objectContent); return $stream; } /** * @param string $urn Unified Resource Name * @return void * @throws Exception from openstack lib when something goes wrong */ public function deleteObject($urn) { $this->init(); // see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242 $this->container->dataObject()->setName($urn)->delete(); } public function deleteContainer($recursive = false) { $this->init(); $this->container->delete($recursive); } } private/Files/ObjectStore/S3ConnectionTrait.php 0000604 00000007125 15247130452 0015531 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\ObjectStore; use Aws\S3\Exception\S3Exception; use Aws\S3\S3Client; trait S3ConnectionTrait { /** @var array */ protected $params; /** @var S3Client */ protected $connection; /** @var string */ protected $id; /** @var string */ protected $bucket; /** @var int */ protected $timeout; protected $test; protected function parseParams($params) { if (empty($params['key']) || empty($params['secret']) || empty($params['bucket'])) { throw new \Exception("Access Key, Secret and Bucket have to be configured."); } $this->id = 'amazon::' . $params['bucket']; $this->test = isset($params['test']); $this->bucket = $params['bucket']; $this->timeout = (!isset($params['timeout'])) ? 15 : $params['timeout']; $params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region']; $params['hostname'] = empty($params['hostname']) ? 's3.amazonaws.com' : $params['hostname']; if (!isset($params['port']) || $params['port'] === '') { $params['port'] = (isset($params['use_ssl']) && $params['use_ssl'] === false) ? 80 : 443; } $this->params = $params; } /** * Returns the connection * * @return S3Client connected client * @throws \Exception if connection could not be made */ protected function getConnection() { if (!is_null($this->connection)) { return $this->connection; } $scheme = (isset($this->params['use_ssl']) && $this->params['use_ssl'] === false) ? 'http' : 'https'; $base_url = $scheme . '://' . $this->params['hostname'] . ':' . $this->params['port'] . '/'; $options = [ 'key' => $this->params['key'], 'secret' => $this->params['secret'], 'base_url' => $base_url, 'region' => $this->params['region'], S3Client::COMMAND_PARAMS => [ 'PathStyle' => isset($this->params['use_path_style']) ? $this->params['use_path_style'] : false, ] ]; if (isset($this->params['proxy'])) { $options[S3Client::REQUEST_OPTIONS] = ['proxy' => $this->params['proxy']]; } $this->connection = S3Client::factory($options); if (!$this->connection->isValidBucketName($this->bucket)) { throw new \Exception("The configured bucket name is invalid."); } if (!$this->connection->doesBucketExist($this->bucket)) { try { $this->connection->createBucket(array( 'Bucket' => $this->bucket )); $this->connection->waitUntilBucketExists(array( 'Bucket' => $this->bucket, 'waiter.interval' => 1, 'waiter.max_attempts' => 15 )); $this->testTimeout(); } catch (S3Exception $e) { \OCP\Util::logException('files_external', $e); throw new \Exception('Creation of bucket failed. ' . $e->getMessage()); } } return $this->connection; } /** * when running the tests wait to let the buckets catch up */ private function testTimeout() { if ($this->test) { sleep($this->timeout); } } } private/Files/Cache/Cache.php 0000604 00000067465 15247130452 0012020 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Florin Peter <github@florin-peter.de> * @author Jens-Christian Fischer <jens-christian.fischer@switch.ch> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author TheSFReader <TheSFReader@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * @author Xuanwo <xuanwo@yunify.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\DB\QueryBuilder\IQueryBuilder; use Doctrine\DBAL\Driver\Statement; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; use \OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchQuery; use OCP\IDBConnection; /** * Metadata cache for a storage * * The cache stores the metadata for all files and folders in a storage and is kept up to date trough the following mechanisms: * * - Scanner: scans the storage and updates the cache where needed * - Watcher: checks for changes made to the filesystem outside of the ownCloud instance and rescans files and folder when a change is detected * - Updater: listens to changes made to the filesystem inside of the ownCloud instance and updates the cache where needed * - ChangePropagator: updates the mtime and etags of parent folders whenever a change to the cache is made to the cache by the updater */ class Cache implements ICache { use MoveFromCacheTrait { MoveFromCacheTrait::moveFromCache as moveFromCacheFallback; } /** * @var array partial data for the cache */ protected $partial = array(); /** * @var string */ protected $storageId; /** * @var Storage $storageCache */ protected $storageCache; /** @var IMimeTypeLoader */ protected $mimetypeLoader; /** * @var IDBConnection */ protected $connection; /** @var QuerySearchHelper */ protected $querySearchHelper; /** * @param \OC\Files\Storage\Storage|string $storage */ public function __construct($storage) { if ($storage instanceof \OC\Files\Storage\Storage) { $this->storageId = $storage->getId(); } else { $this->storageId = $storage; } if (strlen($this->storageId) > 64) { $this->storageId = md5($this->storageId); } $this->storageCache = new Storage($storage); $this->mimetypeLoader = \OC::$server->getMimeTypeLoader(); $this->connection = \OC::$server->getDatabaseConnection(); $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader); } /** * Get the numeric storage id for this cache's storage * * @return int */ public function getNumericStorageId() { return $this->storageCache->getNumericId(); } /** * get the stored metadata of a file or folder * * @param string | int $file either the path of a file or folder or the file id for a file or folder * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache */ public function get($file) { if (is_string($file) or $file == '') { // normalize file $file = $this->normalize($file); $where = 'WHERE `storage` = ? AND `path_hash` = ?'; $params = array($this->getNumericStorageId(), md5($file)); } else { //file id $where = 'WHERE `fileid` = ?'; $params = array($file); } $sql = 'SELECT `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum` FROM `*PREFIX*filecache` ' . $where; $result = $this->connection->executeQuery($sql, $params); $data = $result->fetch(); //FIXME hide this HACK in the next database layer, or just use doctrine and get rid of MDB2 and PDO //PDO returns false, MDB2 returns null, oracle always uses MDB2, so convert null to false if ($data === null) { $data = false; } //merge partial data if (!$data and is_string($file)) { if (isset($this->partial[$file])) { $data = $this->partial[$file]; } return $data; } else { return self::cacheEntryFromData($data, $this->mimetypeLoader); } } /** * Create a CacheEntry from database row * * @param array $data * @param IMimeTypeLoader $mimetypeLoader * @return CacheEntry */ public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) { //fix types $data['fileid'] = (int)$data['fileid']; $data['parent'] = (int)$data['parent']; $data['size'] = 0 + $data['size']; $data['mtime'] = (int)$data['mtime']; $data['storage_mtime'] = (int)$data['storage_mtime']; $data['encryptedVersion'] = (int)$data['encrypted']; $data['encrypted'] = (bool)$data['encrypted']; $data['storage_id'] = $data['storage']; $data['storage'] = (int)$data['storage']; $data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']); $data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']); if ($data['storage_mtime'] == 0) { $data['storage_mtime'] = $data['mtime']; } $data['permissions'] = (int)$data['permissions']; return new CacheEntry($data); } /** * get the metadata of all files stored in $folder * * @param string $folder * @return ICacheEntry[] */ public function getFolderContents($folder) { $fileId = $this->getId($folder); return $this->getFolderContentsById($fileId); } /** * get the metadata of all files stored in $folder * * @param int $fileId the file id of the folder * @return ICacheEntry[] */ public function getFolderContentsById($fileId) { if ($fileId > -1) { $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum` FROM `*PREFIX*filecache` WHERE `parent` = ? ORDER BY `name` ASC'; $result = $this->connection->executeQuery($sql, [$fileId]); $files = $result->fetchAll(); return array_map(function (array $data) { return self::cacheEntryFromData($data, $this->mimetypeLoader);; }, $files); } else { return array(); } } /** * insert or update meta data for a file or folder * * @param string $file * @param array $data * * @return int file id * @throws \RuntimeException */ public function put($file, array $data) { if (($id = $this->getId($file)) > -1) { $this->update($id, $data); return $id; } else { return $this->insert($file, $data); } } /** * insert meta data for a new file or folder * * @param string $file * @param array $data * * @return int file id * @throws \RuntimeException */ public function insert($file, array $data) { // normalize file $file = $this->normalize($file); if (isset($this->partial[$file])) { //add any saved partial data $data = array_merge($this->partial[$file], $data); unset($this->partial[$file]); } $requiredFields = array('size', 'mtime', 'mimetype'); foreach ($requiredFields as $field) { if (!isset($data[$field])) { //data not complete save as partial and return $this->partial[$file] = $data; return -1; } } $data['path'] = $file; $data['parent'] = $this->getParentId($file); $data['name'] = \OC_Util::basename($file); list($queryParts, $params) = $this->buildParts($data); $queryParts[] = '`storage`'; $params[] = $this->getNumericStorageId(); $queryParts = array_map(function ($item) { return trim($item, "`"); }, $queryParts); $values = array_combine($queryParts, $params); if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [ 'storage', 'path_hash', ]) ) { return (int)$this->connection->lastInsertId('*PREFIX*filecache'); } // The file was created in the mean time if (($id = $this->getId($file)) > -1) { $this->update($id, $data); return $id; } else { throw new \RuntimeException('File entry could not be inserted with insertIfNotExist() but could also not be selected with getId() in order to perform an update. Please try again.'); } } /** * update the metadata of an existing file or folder in the cache * * @param int $id the fileid of the existing file or folder * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged */ public function update($id, array $data) { if (isset($data['path'])) { // normalize path $data['path'] = $this->normalize($data['path']); } if (isset($data['name'])) { // normalize path $data['name'] = $this->normalize($data['name']); } list($queryParts, $params) = $this->buildParts($data); // duplicate $params because we need the parts twice in the SQL statement // once for the SET part, once in the WHERE clause $params = array_merge($params, $params); $params[] = $id; // don't update if the data we try to set is the same as the one in the record // some databases (Postgres) don't like superfluous updates $sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' . 'WHERE (' . implode(' <> ? OR ', $queryParts) . ' <> ? OR ' . implode(' IS NULL OR ', $queryParts) . ' IS NULL' . ') AND `fileid` = ? '; $this->connection->executeQuery($sql, $params); } /** * extract query parts and params array from data array * * @param array $data * @return array [$queryParts, $params] * $queryParts: string[], the (escaped) column names to be set in the query * $params: mixed[], the new values for the columns, to be passed as params to the query */ protected function buildParts(array $data) { $fields = array( 'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'storage'); $doNotCopyStorageMTime = false; if (array_key_exists('mtime', $data) && $data['mtime'] === null) { // this horrific magic tells it to not copy storage_mtime to mtime unset($data['mtime']); $doNotCopyStorageMTime = true; } $params = array(); $queryParts = array(); foreach ($data as $name => $value) { if (array_search($name, $fields) !== false) { if ($name === 'path') { $params[] = md5($value); $queryParts[] = '`path_hash`'; } elseif ($name === 'mimetype') { $params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/'))); $queryParts[] = '`mimepart`'; $value = $this->mimetypeLoader->getId($value); } elseif ($name === 'storage_mtime') { if (!$doNotCopyStorageMTime && !isset($data['mtime'])) { $params[] = $value; $queryParts[] = '`mtime`'; } } elseif ($name === 'encrypted') { if (isset($data['encryptedVersion'])) { $value = $data['encryptedVersion']; } else { // Boolean to integer conversion $value = $value ? 1 : 0; } } $params[] = $value; $queryParts[] = '`' . $name . '`'; } } return array($queryParts, $params); } /** * get the file id for a file * * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file * * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing * * @param string $file * @return int */ public function getId($file) { // normalize file $file = $this->normalize($file); $pathHash = md5($file); $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'; $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash)); if ($row = $result->fetch()) { return $row['fileid']; } else { return -1; } } /** * get the id of the parent folder of a file * * @param string $file * @return int */ public function getParentId($file) { if ($file === '') { return -1; } else { $parent = $this->getParentPath($file); return (int)$this->getId($parent); } } private function getParentPath($path) { $parent = dirname($path); if ($parent === '.') { $parent = ''; } return $parent; } /** * check if a file is available in the cache * * @param string $file * @return bool */ public function inCache($file) { return $this->getId($file) != -1; } /** * remove a file or folder from the cache * * when removing a folder from the cache all files and folders inside the folder will be removed as well * * @param string $file */ public function remove($file) { $entry = $this->get($file); $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?'; $this->connection->executeQuery($sql, array($entry['fileid'])); if ($entry['mimetype'] === 'httpd/unix-directory') { $this->removeChildren($entry); } } /** * Get all sub folders of a folder * * @param array $entry the cache entry of the folder to get the subfolders for * @return array[] the cache entries for the subfolders */ private function getSubFolders($entry) { $children = $this->getFolderContentsById($entry['fileid']); return array_filter($children, function ($child) { return $child['mimetype'] === 'httpd/unix-directory'; }); } /** * Recursively remove all children of a folder * * @param array $entry the cache entry of the folder to remove the children of * @throws \OC\DatabaseException */ private function removeChildren($entry) { $subFolders = $this->getSubFolders($entry); foreach ($subFolders as $folder) { $this->removeChildren($folder); } $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `parent` = ?'; $this->connection->executeQuery($sql, array($entry['fileid'])); } /** * Move a file or folder in the cache * * @param string $source * @param string $target */ public function move($source, $target) { $this->moveFromCache($this, $source, $target); } /** * Get the storage id and path needed for a move * * @param string $path * @return array [$storageId, $internalPath] */ protected function getMoveInfo($path) { return [$this->getNumericStorageId(), $path]; } /** * Move a file or folder in the cache * * @param \OCP\Files\Cache\ICache $sourceCache * @param string $sourcePath * @param string $targetPath * @throws \OC\DatabaseException * @throws \Exception if the given storages have an invalid id */ public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { if ($sourceCache instanceof Cache) { // normalize source and target $sourcePath = $this->normalize($sourcePath); $targetPath = $this->normalize($targetPath); $sourceData = $sourceCache->get($sourcePath); $sourceId = $sourceData['fileid']; $newParentId = $this->getParentId($targetPath); list($sourceStorageId, $sourcePath) = $sourceCache->getMoveInfo($sourcePath); list($targetStorageId, $targetPath) = $this->getMoveInfo($targetPath); if (is_null($sourceStorageId) || $sourceStorageId === false) { throw new \Exception('Invalid source storage id: ' . $sourceStorageId); } if (is_null($targetStorageId) || $targetStorageId === false) { throw new \Exception('Invalid target storage id: ' . $targetStorageId); } $this->connection->beginTransaction(); if ($sourceData['mimetype'] === 'httpd/unix-directory') { //update all child entries $sourceLength = mb_strlen($sourcePath); $query = $this->connection->getQueryBuilder(); $fun = $query->func(); $newPathFunction = $fun->concat( $query->createNamedParameter($targetPath), $fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash ); $query->update('filecache') ->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT)) ->set('path_hash', $fun->md5($newPathFunction)) ->set('path', $newPathFunction) ->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%'))); try { $query->execute(); } catch (\OC\DatabaseException $e) { $this->connection->rollBack(); throw $e; } } $sql = 'UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` = ? WHERE `fileid` = ?'; $this->connection->executeQuery($sql, array($targetStorageId, $targetPath, md5($targetPath), \OC_Util::basename($targetPath), $newParentId, $sourceId)); $this->connection->commit(); } else { $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath); } } /** * remove all entries for files that are stored on the storage from the cache */ public function clear() { $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?'; $this->connection->executeQuery($sql, array($this->getNumericStorageId())); $sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?'; $this->connection->executeQuery($sql, array($this->storageId)); } /** * Get the scan status of a file * * - Cache::NOT_FOUND: File is not in the cache * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned * * @param string $file * * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE */ public function getStatus($file) { // normalize file $file = $this->normalize($file); $pathHash = md5($file); $sql = 'SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'; $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash)); if ($row = $result->fetch()) { if ((int)$row['size'] === -1) { return self::SHALLOW; } else { return self::COMPLETE; } } else { if (isset($this->partial[$file])) { return self::PARTIAL; } else { return self::NOT_FOUND; } } } /** * search for files matching $pattern * * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%') * @return ICacheEntry[] an array of cache entries where the name matches the search pattern */ public function search($pattern) { // normalize pattern $pattern = $this->normalize($pattern); if ($pattern === '%%') { return []; } $sql = ' SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `storage_mtime`, `mimepart`, `size`, `mtime`, `encrypted`, `etag`, `permissions`, `checksum` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `name` ILIKE ?'; $result = $this->connection->executeQuery($sql, [$this->getNumericStorageId(), $pattern] ); return $this->searchResultToCacheEntries($result); } /** * @param Statement $result * @return CacheEntry[] */ private function searchResultToCacheEntries(Statement $result) { $files = $result->fetchAll(); return array_map(function (array $data) { return self::cacheEntryFromData($data, $this->mimetypeLoader); }, $files); } /** * search for files by mimetype * * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image') * where it will search for all mimetypes in the group ('image/*') * @return ICacheEntry[] an array of cache entries where the mimetype matches the search */ public function searchByMime($mimetype) { if (strpos($mimetype, '/')) { $where = '`mimetype` = ?'; } else { $where = '`mimepart` = ?'; } $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `storage_mtime`, `mtime`, `encrypted`, `etag`, `permissions`, `checksum` FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?'; $mimetype = $this->mimetypeLoader->getId($mimetype); $result = $this->connection->executeQuery($sql, array($mimetype, $this->getNumericStorageId())); return $this->searchResultToCacheEntries($result); } public function searchQuery(ISearchQuery $searchQuery) { $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); $query = $builder->select(['fileid', 'storage', 'path', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum']) ->from('filecache', 'file'); $query->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->getNumericStorageId()))); if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) { $query ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid')) ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX( $builder->expr()->eq('tagmap.type', 'tag.type'), $builder->expr()->eq('tagmap.categoryid', 'tag.id') )) ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files'))) ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID()))); } $query->andWhere($this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation())); $this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder()); if ($searchQuery->getLimit()) { $query->setMaxResults($searchQuery->getLimit()); } if ($searchQuery->getOffset()) { $query->setFirstResult($searchQuery->getOffset()); } $result = $query->execute(); return $this->searchResultToCacheEntries($result); } /** * Search for files by tag of a given users. * * Note that every user can tag files differently. * * @param string|int $tag name or tag id * @param string $userId owner of the tags * @return ICacheEntry[] file data */ public function searchByTag($tag, $userId) { $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, ' . '`mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, ' . '`encrypted`, `etag`, `permissions`, `checksum` ' . 'FROM `*PREFIX*filecache` `file`, ' . '`*PREFIX*vcategory_to_object` `tagmap`, ' . '`*PREFIX*vcategory` `tag` ' . // JOIN filecache to vcategory_to_object 'WHERE `file`.`fileid` = `tagmap`.`objid` ' . // JOIN vcategory_to_object to vcategory 'AND `tagmap`.`type` = `tag`.`type` ' . 'AND `tagmap`.`categoryid` = `tag`.`id` ' . // conditions 'AND `file`.`storage` = ? ' . 'AND `tag`.`type` = \'files\' ' . 'AND `tag`.`uid` = ? '; if (is_int($tag)) { $sql .= 'AND `tag`.`id` = ? '; } else { $sql .= 'AND `tag`.`category` = ? '; } $result = $this->connection->executeQuery( $sql, [ $this->getNumericStorageId(), $userId, $tag ] ); $files = $result->fetchAll(); return array_map(function (array $data) { return self::cacheEntryFromData($data, $this->mimetypeLoader); }, $files); } /** * Re-calculate the folder size and the size of all parent folders * * @param string|boolean $path * @param array $data (optional) meta data of the folder */ public function correctFolderSize($path, $data = null) { $this->calculateFolderSize($path, $data); if ($path !== '') { $parent = dirname($path); if ($parent === '.' or $parent === '/') { $parent = ''; } $this->correctFolderSize($parent); } } /** * calculate the size of a folder and set it in the cache * * @param string $path * @param array $entry (optional) meta data of the folder * @return int */ public function calculateFolderSize($path, $entry = null) { $totalSize = 0; if (is_null($entry) or !isset($entry['fileid'])) { $entry = $this->get($path); } if (isset($entry['mimetype']) && $entry['mimetype'] === 'httpd/unix-directory') { $id = $entry['fileid']; $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 ' . 'FROM `*PREFIX*filecache` ' . 'WHERE `parent` = ? AND `storage` = ?'; $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId())); if ($row = $result->fetch()) { $result->closeCursor(); list($sum, $min) = array_values($row); $sum = 0 + $sum; $min = 0 + $min; if ($min === -1) { $totalSize = $min; } else { $totalSize = $sum; } $update = array(); if ($entry['size'] !== $totalSize) { $update['size'] = $totalSize; } if (count($update) > 0) { $this->update($id, $update); } } else { $result->closeCursor(); } } return $totalSize; } /** * get all file ids on the files on the storage * * @return int[] */ public function getAll() { $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?'; $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId())); $ids = array(); while ($row = $result->fetch()) { $ids[] = $row['fileid']; } return $ids; } /** * find a folder in the cache which has not been fully scanned * * If multiple incomplete folders are in the cache, the one with the highest id will be returned, * use the one with the highest id gives the best result with the background scanner, since that is most * likely the folder where we stopped scanning previously * * @return string|bool the path of the folder or false when no folder matched */ public function getIncomplete() { $query = $this->connection->prepare('SELECT `path` FROM `*PREFIX*filecache`' . ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC', 1); $query->execute([$this->getNumericStorageId()]); if ($row = $query->fetch()) { return $row['path']; } else { return false; } } /** * get the path of a file on this storage by it's file id * * @param int $id the file id of the file or folder to search * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache */ public function getPathById($id) { $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?'; $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId())); if ($row = $result->fetch()) { // Oracle stores empty strings as null... if ($row['path'] === null) { return ''; } return $row['path']; } else { return null; } } /** * get the storage id of the storage for a file and the internal path of the file * unlike getPathById this does not limit the search to files on this storage and * instead does a global search in the cache table * * @param int $id * @deprecated use getPathById() instead * @return array first element holding the storage id, second the path */ static public function getById($id) { $connection = \OC::$server->getDatabaseConnection(); $sql = 'SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?'; $result = $connection->executeQuery($sql, array($id)); if ($row = $result->fetch()) { $numericId = $row['storage']; $path = $row['path']; } else { return null; } if ($id = Storage::getStorageId($numericId)) { return array($id, $path); } else { return null; } } /** * normalize the given path * * @param string $path * @return string */ public function normalize($path) { return trim(\OC_Util::normalizeUnicode($path), '/'); } } private/Files/Cache/CacheEntry.php 0000604 00000004432 15247130452 0013023 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\Files\Cache\ICacheEntry; /** * meta data for a file or folder */ class CacheEntry implements ICacheEntry, \ArrayAccess { /** * @var array */ private $data; public function __construct(array $data) { $this->data = $data; } public function offsetSet($offset, $value) { $this->data[$offset] = $value; } public function offsetExists($offset) { return isset($this->data[$offset]); } public function offsetUnset($offset) { unset($this->data[$offset]); } public function offsetGet($offset) { if (isset($this->data[$offset])) { return $this->data[$offset]; } else { return null; } } public function getId() { return (int)$this->data['fileid']; } public function getStorageId() { return $this->data['storage']; } public function getPath() { return $this->data['path']; } public function getName() { return $this->data['name']; } public function getMimeType() { return $this->data['mimetype']; } public function getMimePart() { return $this->data['mimepart']; } public function getSize() { return $this->data['size']; } public function getMTime() { return $this->data['mtime']; } public function getStorageMTime() { return $this->data['storage_mtime']; } public function getEtag() { return $this->data['etag']; } public function getPermissions() { return $this->data['permissions']; } public function isEncrypted() { return isset($this->data['encrypted']) && $this->data['encrypted']; } public function getData() { return $this->data; } } private/Files/Cache/Watcher.php 0000604 00000007333 15247130452 0012376 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Daniel Jagszent <daniel@jagszent.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Cache\IWatcher; /** * check the storage backends for updates and change the cache accordingly */ class Watcher implements IWatcher { protected $watchPolicy = self::CHECK_ONCE; protected $checkedPaths = array(); /** * @var \OC\Files\Storage\Storage $storage */ protected $storage; /** * @var Cache $cache */ protected $cache; /** * @var Scanner $scanner ; */ protected $scanner; /** * @param \OC\Files\Storage\Storage $storage */ public function __construct(\OC\Files\Storage\Storage $storage) { $this->storage = $storage; $this->cache = $storage->getCache(); $this->scanner = $storage->getScanner(); } /** * @param int $policy either \OC\Files\Cache\Watcher::CHECK_NEVER, \OC\Files\Cache\Watcher::CHECK_ONCE, \OC\Files\Cache\Watcher::CHECK_ALWAYS */ public function setPolicy($policy) { $this->watchPolicy = $policy; } /** * @return int either \OC\Files\Cache\Watcher::CHECK_NEVER, \OC\Files\Cache\Watcher::CHECK_ONCE, \OC\Files\Cache\Watcher::CHECK_ALWAYS */ public function getPolicy() { return $this->watchPolicy; } /** * check $path for updates and update if needed * * @param string $path * @param ICacheEntry|null $cachedEntry * @return boolean true if path was updated */ public function checkUpdate($path, $cachedEntry = null) { if (is_null($cachedEntry)) { $cachedEntry = $this->cache->get($path); } if ($this->needsUpdate($path, $cachedEntry)) { $this->update($path, $cachedEntry); return true; } else { return false; } } /** * Update the cache for changes to $path * * @param string $path * @param ICacheEntry $cachedData */ public function update($path, $cachedData) { if ($this->storage->is_dir($path)) { $this->scanner->scan($path, Scanner::SCAN_SHALLOW); } else { $this->scanner->scanFile($path); } if ($cachedData['mimetype'] === 'httpd/unix-directory') { $this->cleanFolder($path); } if ($this->cache instanceof Cache) { $this->cache->correctFolderSize($path); } } /** * Check if the cache for $path needs to be updated * * @param string $path * @param ICacheEntry $cachedData * @return bool */ public function needsUpdate($path, $cachedData) { if ($this->watchPolicy === self::CHECK_ALWAYS or ($this->watchPolicy === self::CHECK_ONCE and array_search($path, $this->checkedPaths) === false)) { $this->checkedPaths[] = $path; return $this->storage->hasUpdated($path, $cachedData['storage_mtime']); } return false; } /** * remove deleted files in $path from the cache * * @param string $path */ public function cleanFolder($path) { $cachedContent = $this->cache->getFolderContents($path); foreach ($cachedContent as $entry) { if (!$this->storage->file_exists($entry['path'])) { $this->cache->remove($entry['path']); } } } } private/Files/Cache/Scanner.php 0000604 00000041366 15247130452 0012376 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Björn Schießle <bjoern@schiessle.org> * @author Daniel Jagszent <daniel@jagszent.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Martin Mattel <martin.mattel@diemattels.at> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Owen Winkler <a_github@midnightcircus.com> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OC\Files\Filesystem; use OC\Hooks\BasicEmitter; use OCP\Config; use OCP\Files\Cache\IScanner; use OCP\Files\ForbiddenException; use OCP\Lock\ILockingProvider; /** * Class Scanner * * Hooks available in scope \OC\Files\Cache\Scanner: * - scanFile(string $path, string $storageId) * - scanFolder(string $path, string $storageId) * - postScanFile(string $path, string $storageId) * - postScanFolder(string $path, string $storageId) * * @package OC\Files\Cache */ class Scanner extends BasicEmitter implements IScanner { /** * @var \OC\Files\Storage\Storage $storage */ protected $storage; /** * @var string $storageId */ protected $storageId; /** * @var \OC\Files\Cache\Cache $cache */ protected $cache; /** * @var boolean $cacheActive If true, perform cache operations, if false, do not affect cache */ protected $cacheActive; /** * @var bool $useTransactions whether to use transactions */ protected $useTransactions = true; /** * @var \OCP\Lock\ILockingProvider */ protected $lockingProvider; public function __construct(\OC\Files\Storage\Storage $storage) { $this->storage = $storage; $this->storageId = $this->storage->getId(); $this->cache = $storage->getCache(); $this->cacheActive = !Config::getSystemValue('filesystem_cache_readonly', false); $this->lockingProvider = \OC::$server->getLockingProvider(); } /** * Whether to wrap the scanning of a folder in a database transaction * On default transactions are used * * @param bool $useTransactions */ public function setUseTransactions($useTransactions) { $this->useTransactions = $useTransactions; } /** * get all the metadata of a file or folder * * * * @param string $path * @return array an array of metadata of the file */ protected function getData($path) { $data = $this->storage->getMetaData($path); if (is_null($data)) { \OCP\Util::writeLog('OC\Files\Cache\Scanner', "!!! Path '$path' is not accessible or present !!!", \OCP\Util::DEBUG); } return $data; } /** * scan a single file and store it in the cache * * @param string $file * @param int $reuseExisting * @param int $parentId * @param array | null $cacheData existing data in the cache for the file to be scanned * @param bool $lock set to false to disable getting an additional read lock during scanning * @return array an array of metadata of the scanned file * @throws \OC\ServerNotAvailableException * @throws \OCP\Lock\LockedException */ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) { if ($file !== '') { try { $this->storage->verifyPath(dirname($file), basename($file)); } catch (\Exception $e) { return null; } } // only proceed if $file is not a partial file nor a blacklisted file if (!self::isPartialFile($file) and !Filesystem::isFileBlacklisted($file)) { //acquire a lock if ($lock) { if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $this->storage->acquireLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider); } } try { $data = $this->getData($file); } catch (ForbiddenException $e) { return null; } if ($data) { // pre-emit only if it was a file. By that we avoid counting/treating folders as files if ($data['mimetype'] !== 'httpd/unix-directory') { $this->emit('\OC\Files\Cache\Scanner', 'scanFile', array($file, $this->storageId)); \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); } $parent = dirname($file); if ($parent === '.' or $parent === '/') { $parent = ''; } if ($parentId === -1) { $parentId = $this->cache->getParentId($file); } // scan the parent if it's not in the cache (id -1) and the current file is not the root folder if ($file and $parentId === -1) { $parentData = $this->scanFile($parent); if (!$parentData) { return null; } $parentId = $parentData['fileid']; } if ($parent) { $data['parent'] = $parentId; } if (is_null($cacheData)) { /** @var CacheEntry $cacheData */ $cacheData = $this->cache->get($file); } if ($cacheData and $reuseExisting and isset($cacheData['fileid'])) { // prevent empty etag if (empty($cacheData['etag'])) { $etag = $data['etag']; } else { $etag = $cacheData['etag']; } $fileId = $cacheData['fileid']; $data['fileid'] = $fileId; // only reuse data if the file hasn't explicitly changed if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) { $data['mtime'] = $cacheData['mtime']; if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) { $data['size'] = $cacheData['size']; } if ($reuseExisting & self::REUSE_ETAG) { $data['etag'] = $etag; } } // Only update metadata that has changed $newData = array_diff_assoc($data, $cacheData->getData()); } else { $newData = $data; $fileId = -1; } if (!empty($newData)) { // Reset the checksum if the data has changed $newData['checksum'] = ''; $data['fileid'] = $this->addToCache($file, $newData, $fileId); } if (isset($cacheData['size'])) { $data['oldSize'] = $cacheData['size']; } else { $data['oldSize'] = 0; } if (isset($cacheData['encrypted'])) { $data['encrypted'] = $cacheData['encrypted']; } // post-emit only if it was a file. By that we avoid counting/treating folders as files if ($data['mimetype'] !== 'httpd/unix-directory') { $this->emit('\OC\Files\Cache\Scanner', 'postScanFile', array($file, $this->storageId)); \OC_Hook::emit('\OC\Files\Cache\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId)); } } else { $this->removeFromCache($file); } //release the acquired lock if ($lock) { if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $this->storage->releaseLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider); } } if ($data && !isset($data['encrypted'])) { $data['encrypted'] = false; } return $data; } return null; } protected function removeFromCache($path) { \OC_Hook::emit('Scanner', 'removeFromCache', array('file' => $path)); $this->emit('\OC\Files\Cache\Scanner', 'removeFromCache', array($path)); if ($this->cacheActive) { $this->cache->remove($path); } } /** * @param string $path * @param array $data * @param int $fileId * @return int the id of the added file */ protected function addToCache($path, $data, $fileId = -1) { if (isset($data['scan_permissions'])) { $data['permissions'] = $data['scan_permissions']; } \OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data)); $this->emit('\OC\Files\Cache\Scanner', 'addToCache', array($path, $this->storageId, $data)); if ($this->cacheActive) { if ($fileId !== -1) { $this->cache->update($fileId, $data); return $fileId; } else { return $this->cache->put($path, $data); } } else { return -1; } } /** * @param string $path * @param array $data * @param int $fileId */ protected function updateCache($path, $data, $fileId = -1) { \OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data)); $this->emit('\OC\Files\Cache\Scanner', 'updateCache', array($path, $this->storageId, $data)); if ($this->cacheActive) { if ($fileId !== -1) { $this->cache->update($fileId, $data); } else { $this->cache->put($path, $data); } } } /** * scan a folder and all it's children * * @param string $path * @param bool $recursive * @param int $reuse * @param bool $lock set to false to disable getting an additional read lock during scanning * @return array an array of the meta data of the scanned file or folder */ public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { if ($reuse === -1) { $reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG; } if ($lock) { if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $this->storage->acquireLock('scanner::' . $path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider); $this->storage->acquireLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider); } } $data = $this->scanFile($path, $reuse, -1, null, $lock); if ($data and $data['mimetype'] === 'httpd/unix-directory') { $size = $this->scanChildren($path, $recursive, $reuse, $data['fileid'], $lock); $data['size'] = $size; } if ($lock) { if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $this->storage->releaseLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider); $this->storage->releaseLock('scanner::' . $path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider); } } return $data; } /** * Get the children currently in the cache * * @param int $folderId * @return array[] */ protected function getExistingChildren($folderId) { $existingChildren = array(); $children = $this->cache->getFolderContentsById($folderId); foreach ($children as $child) { $existingChildren[$child['name']] = $child; } return $existingChildren; } /** * Get the children from the storage * * @param string $folder * @return string[] */ protected function getNewChildren($folder) { $children = array(); if ($dh = $this->storage->opendir($folder)) { if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { if (!Filesystem::isIgnoredDir($file)) { $children[] = trim(\OC\Files\Filesystem::normalizePath($file), '/'); } } } } return $children; } /** * scan all the files and folders in a folder * * @param string $path * @param bool $recursive * @param int $reuse * @param int $folderId id for the folder to be scanned * @param bool $lock set to false to disable getting an additional read lock during scanning * @return int the size of the scanned folder or -1 if the size is unknown at this stage */ protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true) { if ($reuse === -1) { $reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG; } $this->emit('\OC\Files\Cache\Scanner', 'scanFolder', array($path, $this->storageId)); $size = 0; if (!is_null($folderId)) { $folderId = $this->cache->getId($path); } $childQueue = $this->handleChildren($path, $recursive, $reuse, $folderId, $lock, $size); foreach ($childQueue as $child => $childId) { $childSize = $this->scanChildren($child, $recursive, $reuse, $childId, $lock); if ($childSize === -1) { $size = -1; } else if ($size !== -1) { $size += $childSize; } } if ($this->cacheActive) { $this->cache->update($folderId, array('size' => $size)); } $this->emit('\OC\Files\Cache\Scanner', 'postScanFolder', array($path, $this->storageId)); return $size; } private function handleChildren($path, $recursive, $reuse, $folderId, $lock, &$size) { // we put this in it's own function so it cleans up the memory before we start recursing $existingChildren = $this->getExistingChildren($folderId); $newChildren = $this->getNewChildren($path); if ($this->useTransactions) { \OC::$server->getDatabaseConnection()->beginTransaction(); } $exceptionOccurred = false; $childQueue = []; foreach ($newChildren as $file) { $child = ($path) ? $path . '/' . $file : $file; try { $existingData = isset($existingChildren[$file]) ? $existingChildren[$file] : null; $data = $this->scanFile($child, $reuse, $folderId, $existingData, $lock); if ($data) { if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE) { $childQueue[$child] = $data['fileid']; } else if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE_INCOMPLETE and $data['size'] === -1) { // only recurse into folders which aren't fully scanned $childQueue[$child] = $data['fileid']; } else if ($data['size'] === -1) { $size = -1; } else if ($size !== -1) { $size += $data['size']; } } } catch (\Doctrine\DBAL\DBALException $ex) { // might happen if inserting duplicate while a scanning // process is running in parallel // log and ignore \OCP\Util::writeLog('core', 'Exception while scanning file "' . $child . '": ' . $ex->getMessage(), \OCP\Util::DEBUG); $exceptionOccurred = true; } catch (\OCP\Lock\LockedException $e) { if ($this->useTransactions) { \OC::$server->getDatabaseConnection()->rollback(); } throw $e; } } $removedChildren = \array_diff(array_keys($existingChildren), $newChildren); foreach ($removedChildren as $childName) { $child = ($path) ? $path . '/' . $childName : $childName; $this->removeFromCache($child); } if ($this->useTransactions) { \OC::$server->getDatabaseConnection()->commit(); } if ($exceptionOccurred) { // It might happen that the parallel scan process has already // inserted mimetypes but those weren't available yet inside the transaction // To make sure to have the updated mime types in such cases, // we reload them here \OC::$server->getMimeTypeLoader()->reset(); } return $childQueue; } /** * check if the file should be ignored when scanning * NOTE: files with a '.part' extension are ignored as well! * prevents unfinished put requests to be scanned * * @param string $file * @return boolean */ public static function isPartialFile($file) { if (pathinfo($file, PATHINFO_EXTENSION) === 'part') { return true; } if (strpos($file, '.part/') !== false) { return true; } return false; } /** * walk over any folders that are not fully scanned yet and scan them */ public function backgroundScan() { if (!$this->cache->inCache('')) { $this->runBackgroundScanJob(function () { $this->scan('', self::SCAN_RECURSIVE, self::REUSE_ETAG); }, ''); } else { $lastPath = null; while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) { $this->runBackgroundScanJob(function () use ($path) { $this->scan($path, self::SCAN_RECURSIVE_INCOMPLETE, self::REUSE_ETAG | self::REUSE_SIZE); }, $path); // FIXME: this won't proceed with the next item, needs revamping of getIncomplete() // to make this possible $lastPath = $path; } } } private function runBackgroundScanJob(callable $callback, $path) { try { $callback(); \OC_Hook::emit('Scanner', 'correctFolderSize', array('path' => $path)); if ($this->cacheActive && $this->cache instanceof Cache) { $this->cache->correctFolderSize($path); } } catch (\OCP\Files\StorageInvalidException $e) { // skip unavailable storages } catch (\OCP\Files\StorageNotAvailableException $e) { // skip unavailable storages } catch (\OCP\Files\ForbiddenException $e) { // skip forbidden storages } catch (\OCP\Lock\LockedException $e) { // skip unavailable storages } } /** * Set whether the cache is affected by scan operations * * @param boolean $active The active state of the cache */ public function setCacheActive($active) { $this->cacheActive = $active; } } private/Files/Cache/QuerySearchHelper.php 0000604 00000016353 15247130452 0014376 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\Cache; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchOrder; /** * Tools for transforming search queries into database queries */ class QuerySearchHelper { static protected $searchOperatorMap = [ ISearchComparison::COMPARE_LIKE => 'iLike', ISearchComparison::COMPARE_EQUAL => 'eq', ISearchComparison::COMPARE_GREATER_THAN => 'gt', ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'gte', ISearchComparison::COMPARE_LESS_THAN => 'lt', ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lte' ]; static protected $searchOperatorNegativeMap = [ ISearchComparison::COMPARE_LIKE => 'notLike', ISearchComparison::COMPARE_EQUAL => 'neq', ISearchComparison::COMPARE_GREATER_THAN => 'lte', ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'lt', ISearchComparison::COMPARE_LESS_THAN => 'gte', ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lt' ]; const TAG_FAVORITE = '_$!<Favorite>!$_'; /** @var IMimeTypeLoader */ private $mimetypeLoader; /** * QuerySearchUtil constructor. * * @param IMimeTypeLoader $mimetypeLoader */ public function __construct(IMimeTypeLoader $mimetypeLoader) { $this->mimetypeLoader = $mimetypeLoader; } /** * Whether or not the tag tables should be joined to complete the search * * @param ISearchOperator $operator * @return boolean */ public function shouldJoinTags(ISearchOperator $operator) { if ($operator instanceof ISearchBinaryOperator) { return array_reduce($operator->getArguments(), function ($shouldJoin, ISearchOperator $operator) { return $shouldJoin || $this->shouldJoinTags($operator); }, false); } else if ($operator instanceof ISearchComparison) { return $operator->getField() === 'tagname' || $operator->getField() === 'favorite'; } return false; } public function searchOperatorToDBExpr(IQueryBuilder $builder, ISearchOperator $operator) { $expr = $builder->expr(); if ($operator instanceof ISearchBinaryOperator) { switch ($operator->getType()) { case ISearchBinaryOperator::OPERATOR_NOT: $negativeOperator = $operator->getArguments()[0]; if ($negativeOperator instanceof ISearchComparison) { return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::$searchOperatorNegativeMap); } else { throw new \InvalidArgumentException('Binary operators inside "not" is not supported'); } case ISearchBinaryOperator::OPERATOR_AND: return $expr->andX($this->searchOperatorToDBExpr($builder, $operator->getArguments()[0]), $this->searchOperatorToDBExpr($builder, $operator->getArguments()[1])); case ISearchBinaryOperator::OPERATOR_OR: return $expr->orX($this->searchOperatorToDBExpr($builder, $operator->getArguments()[0]), $this->searchOperatorToDBExpr($builder, $operator->getArguments()[1])); default: throw new \InvalidArgumentException('Invalid operator type: ' . $operator->getType()); } } else if ($operator instanceof ISearchComparison) { return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap); } else { throw new \InvalidArgumentException('Invalid operator type: ' . get_class($operator)); } } private function searchComparisonToDBExpr(IQueryBuilder $builder, ISearchComparison $comparison, array $operatorMap) { $this->validateComparison($comparison); list($field, $value, $type) = $this->getOperatorFieldAndValue($comparison); if (isset($operatorMap[$type])) { $queryOperator = $operatorMap[$type]; return $builder->expr()->$queryOperator($field, $this->getParameterForValue($builder, $value)); } else { throw new \InvalidArgumentException('Invalid operator type: ' . $comparison->getType()); } } private function getOperatorFieldAndValue(ISearchComparison $operator) { $field = $operator->getField(); $value = $operator->getValue(); $type = $operator->getType(); if ($field === 'mimetype') { if ($operator->getType() === ISearchComparison::COMPARE_EQUAL) { $value = $this->mimetypeLoader->getId($value); } else if ($operator->getType() === ISearchComparison::COMPARE_LIKE) { // transform "mimetype='foo/%'" to "mimepart='foo'" if (preg_match('|(.+)/%|', $value, $matches)) { $field = 'mimepart'; $value = $this->mimetypeLoader->getId($matches[1]); $type = ISearchComparison::COMPARE_EQUAL; } if (strpos($value, '%') !== false) { throw new \InvalidArgumentException('Unsupported query value for mimetype: ' . $value . ', only values in the format "mime/type" or "mime/%" are supported'); } } } else if ($field === 'favorite') { $field = 'tag.category'; $value = self::TAG_FAVORITE; } else if ($field === 'tagname') { $field = 'tag.category'; } return [$field, $value, $type]; } private function validateComparison(ISearchComparison $operator) { $types = [ 'mimetype' => 'string', 'mtime' => 'integer', 'name' => 'string', 'size' => 'integer', 'tagname' => 'string', 'favorite' => 'boolean', 'fileid' => 'integer' ]; $comparisons = [ 'mimetype' => ['eq', 'like'], 'mtime' => ['eq', 'gt', 'lt', 'gte', 'lte'], 'name' => ['eq', 'like'], 'size' => ['eq', 'gt', 'lt', 'gte', 'lte'], 'tagname' => ['eq', 'like'], 'favorite' => ['eq'], 'fileid' => ['eq'] ]; if (!isset($types[$operator->getField()])) { throw new \InvalidArgumentException('Unsupported comparison field ' . $operator->getField()); } $type = $types[$operator->getField()]; if (gettype($operator->getValue()) !== $type) { throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField()); } if (!in_array($operator->getType(), $comparisons[$operator->getField()])) { throw new \InvalidArgumentException('Unsupported comparison for field ' . $operator->getField() . ': ' . $operator->getType()); } } private function getParameterForValue(IQueryBuilder $builder, $value) { if ($value instanceof \DateTime) { $value = $value->getTimestamp(); } if (is_numeric($value)) { $type = IQueryBuilder::PARAM_INT; } else { $type = IQueryBuilder::PARAM_STR; } return $builder->createNamedParameter($value, $type); } /** * @param IQueryBuilder $query * @param ISearchOrder[] $orders */ public function addSearchOrdersToQuery(IQueryBuilder $query, array $orders) { foreach ($orders as $order) { $query->addOrderBy($order->getField(), $order->getDirection()); } } } private/Files/Cache/HomeCache.php 0000604 00000005206 15247130452 0012612 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Björn Schießle <bjoern@schiessle.org> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\Files\Cache\ICacheEntry; class HomeCache extends Cache { /** * get the size of a folder and set it in the cache * * @param string $path * @param array $entry (optional) meta data of the folder * @return int */ public function calculateFolderSize($path, $entry = null) { if ($path !== '/' and $path !== '' and $path !== 'files' and $path !== 'files_trashbin' and $path !== 'files_versions') { return parent::calculateFolderSize($path, $entry); } elseif ($path === '' or $path === '/') { // since the size of / isn't used (the size of /files is used instead) there is no use in calculating it return 0; } $totalSize = 0; if (is_null($entry)) { $entry = $this->get($path); } if ($entry && $entry['mimetype'] === 'httpd/unix-directory') { $id = $entry['fileid']; $sql = 'SELECT SUM(`size`) AS f1 ' . 'FROM `*PREFIX*filecache` ' . 'WHERE `parent` = ? AND `storage` = ? AND `size` >= 0'; $result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId())); if ($row = $result->fetchRow()) { $result->closeCursor(); list($sum) = array_values($row); $totalSize = 0 + $sum; $entry['size'] += 0; if ($entry['size'] !== $totalSize) { $this->update($id, array('size' => $totalSize)); } } } return $totalSize; } /** * @param string $path * @return ICacheEntry */ public function get($path) { $data = parent::get($path); if ($path === '' or $path === '/') { // only the size of the "files" dir counts $filesData = parent::get('files'); if (isset($filesData['size'])) { $data['size'] = $filesData['size']; } } return $data; } } private/Files/Cache/MoveFromCacheTrait.php 0000604 00000005036 15247130452 0014461 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; /** * Fallback implementation for moveFromCache */ trait MoveFromCacheTrait { /** * store meta data for a file or folder * * @param string $file * @param array $data * * @return int file id * @throws \RuntimeException */ abstract public function put($file, array $data); /** * Move a file or folder in the cache * * @param \OCP\Files\Cache\ICache $sourceCache * @param string $sourcePath * @param string $targetPath */ public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { $sourceEntry = $sourceCache->get($sourcePath); $this->copyFromCache($sourceCache, $sourceEntry, $targetPath); $sourceCache->remove($sourcePath); } /** * Copy a file or folder in the cache * * @param \OCP\Files\Cache\ICache $sourceCache * @param ICacheEntry $sourceEntry * @param string $targetPath */ public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, $targetPath) { $this->put($targetPath, $this->cacheEntryToArray($sourceEntry)); if ($sourceEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) { $folderContent = $sourceCache->getFolderContentsById($sourceEntry->getId()); foreach ($folderContent as $subEntry) { $subTargetPath = $targetPath . '/' . $subEntry->getName(); $this->copyFromCache($sourceCache, $subEntry, $subTargetPath); } } } private function cacheEntryToArray(ICacheEntry $entry) { return [ 'size' => $entry->getSize(), 'mtime' => $entry->getMTime(), 'storage_mtime' => $entry->getStorageMTime(), 'mimetype' => $entry->getMimeType(), 'mimepart' => $entry->getMimePart(), 'etag' => $entry->getEtag(), 'permissions' => $entry->getPermissions(), 'encrypted' => $entry->isEncrypted() ]; } } private/Files/Cache/Wrapper/CacheJail.php 0000604 00000020031 15247130452 0014212 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Daniel Jagszent <daniel@jagszent.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache\Wrapper; use OC\Files\Cache\Cache; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Search\ISearchQuery; /** * Jail to a subdirectory of the wrapped cache */ class CacheJail extends CacheWrapper { /** * @var string */ protected $root; /** * @param \OCP\Files\Cache\ICache $cache * @param string $root */ public function __construct($cache, $root) { parent::__construct($cache); $this->root = $root; } protected function getSourcePath($path) { if ($path === '') { return $this->root; } else { return $this->root . '/' . ltrim($path, '/'); } } /** * @param string $path * @return null|string the jailed path or null if the path is outside the jail */ protected function getJailedPath($path) { if ($this->root === '') { return $path; } $rootLength = strlen($this->root) + 1; if ($path === $this->root) { return ''; } else if (substr($path, 0, $rootLength) === $this->root . '/') { return substr($path, $rootLength); } else { return null; } } /** * @param ICacheEntry|array $entry * @return array */ protected function formatCacheEntry($entry) { if (isset($entry['path'])) { $entry['path'] = $this->getJailedPath($entry['path']); } return $entry; } protected function filterCacheEntry($entry) { $rootLength = strlen($this->root) + 1; return ($entry['path'] === $this->root) or (substr($entry['path'], 0, $rootLength) === $this->root . '/'); } /** * get the stored metadata of a file or folder * * @param string /int $file * @return ICacheEntry|false */ public function get($file) { if (is_string($file) or $file == '') { $file = $this->getSourcePath($file); } return parent::get($file); } /** * insert meta data for a new file or folder * * @param string $file * @param array $data * * @return int file id * @throws \RuntimeException */ public function insert($file, array $data) { return $this->getCache()->insert($this->getSourcePath($file), $data); } /** * update the metadata in the cache * * @param int $id * @param array $data */ public function update($id, array $data) { $this->getCache()->update($id, $data); } /** * get the file id for a file * * @param string $file * @return int */ public function getId($file) { return $this->getCache()->getId($this->getSourcePath($file)); } /** * get the id of the parent folder of a file * * @param string $file * @return int */ public function getParentId($file) { return $this->getCache()->getParentId($this->getSourcePath($file)); } /** * check if a file is available in the cache * * @param string $file * @return bool */ public function inCache($file) { return $this->getCache()->inCache($this->getSourcePath($file)); } /** * remove a file or folder from the cache * * @param string $file */ public function remove($file) { $this->getCache()->remove($this->getSourcePath($file)); } /** * Move a file or folder in the cache * * @param string $source * @param string $target */ public function move($source, $target) { $this->getCache()->move($this->getSourcePath($source), $this->getSourcePath($target)); } /** * Get the storage id and path needed for a move * * @param string $path * @return array [$storageId, $internalPath] */ protected function getMoveInfo($path) { return [$this->getNumericStorageId(), $this->getSourcePath($path)]; } /** * remove all entries for files that are stored on the storage from the cache */ public function clear() { $this->getCache()->remove($this->root); } /** * @param string $file * * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE */ public function getStatus($file) { return $this->getCache()->getStatus($this->getSourcePath($file)); } private function formatSearchResults($results) { $results = array_filter($results, array($this, 'filterCacheEntry')); $results = array_values($results); return array_map(array($this, 'formatCacheEntry'), $results); } /** * search for files matching $pattern * * @param string $pattern * @return array an array of file data */ public function search($pattern) { $results = $this->getCache()->search($pattern); return $this->formatSearchResults($results); } /** * search for files by mimetype * * @param string $mimetype * @return array */ public function searchByMime($mimetype) { $results = $this->getCache()->searchByMime($mimetype); return $this->formatSearchResults($results); } public function searchQuery(ISearchQuery $query) { $results = $this->getCache()->searchQuery($query); return $this->formatSearchResults($results); } /** * search for files by mimetype * * @param string|int $tag name or tag id * @param string $userId owner of the tags * @return array */ public function searchByTag($tag, $userId) { $results = $this->getCache()->searchByTag($tag, $userId); return $this->formatSearchResults($results); } /** * update the folder size and the size of all parent folders * * @param string|boolean $path * @param array $data (optional) meta data of the folder */ public function correctFolderSize($path, $data = null) { if ($this->getCache() instanceof Cache) { $this->getCache()->correctFolderSize($this->getSourcePath($path), $data); } } /** * get the size of a folder and set it in the cache * * @param string $path * @param array $entry (optional) meta data of the folder * @return int */ public function calculateFolderSize($path, $entry = null) { if ($this->getCache() instanceof Cache) { return $this->getCache()->calculateFolderSize($this->getSourcePath($path), $entry); } else { return 0; } } /** * get all file ids on the files on the storage * * @return int[] */ public function getAll() { // not supported return array(); } /** * find a folder in the cache which has not been fully scanned * * If multiply incomplete folders are in the cache, the one with the highest id will be returned, * use the one with the highest id gives the best result with the background scanner, since that is most * likely the folder where we stopped scanning previously * * @return string|bool the path of the folder or false when no folder matched */ public function getIncomplete() { // not supported return false; } /** * get the path of a file on this storage by it's id * * @param int $id * @return string|null */ public function getPathById($id) { $path = $this->getCache()->getPathById($id); return $this->getJailedPath($path); } /** * Move a file or folder in the cache * * Note that this should make sure the entries are removed from the source cache * * @param \OCP\Files\Cache\ICache $sourceCache * @param string $sourcePath * @param string $targetPath */ public function moveFromCache(\OCP\Files\Cache\ICache $sourceCache, $sourcePath, $targetPath) { if ($sourceCache === $this) { return $this->move($sourcePath, $targetPath); } return $this->getCache()->moveFromCache($sourceCache, $sourcePath, $this->getSourcePath($targetPath)); } } private/Files/Cache/Wrapper/CacheWrapper.php 0000604 00000017564 15247130452 0014774 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Daniel Jagszent <daniel@jagszent.de> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Stefan Weil <sw@weilnetz.de> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache\Wrapper; use OC\Files\Cache\Cache; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Cache\ICache; use OCP\Files\Search\ISearchQuery; class CacheWrapper extends Cache { /** * @var \OCP\Files\Cache\ICache */ protected $cache; /** * @param \OCP\Files\Cache\ICache $cache */ public function __construct($cache) { $this->cache = $cache; } protected function getCache() { return $this->cache; } /** * Make it easy for wrappers to modify every returned cache entry * * @param ICacheEntry $entry * @return ICacheEntry */ protected function formatCacheEntry($entry) { return $entry; } /** * get the stored metadata of a file or folder * * @param string|int $file * @return ICacheEntry|false */ public function get($file) { $result = $this->getCache()->get($file); if ($result) { $result = $this->formatCacheEntry($result); } return $result; } /** * get the metadata of all files stored in $folder * * @param string $folder * @return ICacheEntry[] */ public function getFolderContents($folder) { // can't do a simple $this->getCache()->.... call here since getFolderContentsById needs to be called on this // and not the wrapped cache $fileId = $this->getId($folder); return $this->getFolderContentsById($fileId); } /** * get the metadata of all files stored in $folder * * @param int $fileId the file id of the folder * @return array */ public function getFolderContentsById($fileId) { $results = $this->getCache()->getFolderContentsById($fileId); return array_map(array($this, 'formatCacheEntry'), $results); } /** * insert or update meta data for a file or folder * * @param string $file * @param array $data * * @return int file id * @throws \RuntimeException */ public function put($file, array $data) { if (($id = $this->getId($file)) > -1) { $this->update($id, $data); return $id; } else { return $this->insert($file, $data); } } /** * insert meta data for a new file or folder * * @param string $file * @param array $data * * @return int file id * @throws \RuntimeException */ public function insert($file, array $data) { return $this->getCache()->insert($file, $data); } /** * update the metadata in the cache * * @param int $id * @param array $data */ public function update($id, array $data) { $this->getCache()->update($id, $data); } /** * get the file id for a file * * @param string $file * @return int */ public function getId($file) { return $this->getCache()->getId($file); } /** * get the id of the parent folder of a file * * @param string $file * @return int */ public function getParentId($file) { return $this->getCache()->getParentId($file); } /** * check if a file is available in the cache * * @param string $file * @return bool */ public function inCache($file) { return $this->getCache()->inCache($file); } /** * remove a file or folder from the cache * * @param string $file */ public function remove($file) { $this->getCache()->remove($file); } /** * Move a file or folder in the cache * * @param string $source * @param string $target */ public function move($source, $target) { $this->getCache()->move($source, $target); } public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { $this->getCache()->moveFromCache($sourceCache, $sourcePath, $targetPath); } /** * remove all entries for files that are stored on the storage from the cache */ public function clear() { $this->getCache()->clear(); } /** * @param string $file * * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE */ public function getStatus($file) { return $this->getCache()->getStatus($file); } /** * search for files matching $pattern * * @param string $pattern * @return ICacheEntry[] an array of file data */ public function search($pattern) { $results = $this->getCache()->search($pattern); return array_map(array($this, 'formatCacheEntry'), $results); } /** * search for files by mimetype * * @param string $mimetype * @return ICacheEntry[] */ public function searchByMime($mimetype) { $results = $this->getCache()->searchByMime($mimetype); return array_map(array($this, 'formatCacheEntry'), $results); } public function searchQuery(ISearchQuery $query) { $results = $this->getCache()->searchQuery($query); return array_map(array($this, 'formatCacheEntry'), $results); } /** * search for files by tag * * @param string|int $tag name or tag id * @param string $userId owner of the tags * @return ICacheEntry[] file data */ public function searchByTag($tag, $userId) { $results = $this->getCache()->searchByTag($tag, $userId); return array_map(array($this, 'formatCacheEntry'), $results); } /** * update the folder size and the size of all parent folders * * @param string|boolean $path * @param array $data (optional) meta data of the folder */ public function correctFolderSize($path, $data = null) { if ($this->getCache() instanceof Cache) { $this->getCache()->correctFolderSize($path, $data); } } /** * get the size of a folder and set it in the cache * * @param string $path * @param array $entry (optional) meta data of the folder * @return int */ public function calculateFolderSize($path, $entry = null) { if ($this->getCache() instanceof Cache) { return $this->getCache()->calculateFolderSize($path, $entry); } else { return 0; } } /** * get all file ids on the files on the storage * * @return int[] */ public function getAll() { return $this->getCache()->getAll(); } /** * find a folder in the cache which has not been fully scanned * * If multiple incomplete folders are in the cache, the one with the highest id will be returned, * use the one with the highest id gives the best result with the background scanner, since that is most * likely the folder where we stopped scanning previously * * @return string|bool the path of the folder or false when no folder matched */ public function getIncomplete() { return $this->getCache()->getIncomplete(); } /** * get the path of a file on this storage by it's id * * @param int $id * @return string|null */ public function getPathById($id) { return $this->getCache()->getPathById($id); } /** * Returns the numeric storage id * * @return int */ public function getNumericStorageId() { return $this->getCache()->getNumericStorageId(); } /** * get the storage id of the storage for a file and the internal path of the file * unlike getPathById this does not limit the search to files on this storage and * instead does a global search in the cache table * * @param int $id * @return array first element holding the storage id, second the path */ static public function getById($id) { return parent::getById($id); } } private/Files/Cache/Wrapper/JailPropagator.php 0000604 00000002572 15247130452 0015337 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Files\Cache\Wrapper; use OC\Files\Cache\Propagator; use OC\Files\Storage\Wrapper\Jail; class JailPropagator extends Propagator { /** * @var Jail */ protected $storage; /** * @param string $internalPath * @param int $time * @param int $sizeDifference */ public function propagateChange($internalPath, $time, $sizeDifference = 0) { /** @var \OC\Files\Storage\Storage $storage */ list($storage, $sourceInternalPath) = $this->storage->resolvePath($internalPath); $storage->getPropagator()->propagateChange($sourceInternalPath, $time, $sizeDifference); } } private/Files/Cache/Wrapper/CachePermissionsMask.php 0000604 00000002435 15247130452 0016472 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache\Wrapper; class CachePermissionsMask extends CacheWrapper { /** * @var int */ protected $mask; /** * @param \OCP\Files\Cache\ICache $cache * @param int $mask */ public function __construct($cache, $mask) { parent::__construct($cache); $this->mask = $mask; } protected function formatCacheEntry($entry) { if (isset($entry['permissions'])) { $entry['scan_permissions'] = $entry['permissions']; $entry['permissions'] &= $this->mask; } return $entry; } } private/Files/Cache/Propagator.php 0000604 00000013457 15247130452 0013123 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Cache\IPropagator; use OCP\IDBConnection; /** * Propagate etags and mtimes within the storage */ class Propagator implements IPropagator { private $inBatch = false; private $batch = []; /** * @var \OC\Files\Storage\Storage */ protected $storage; /** * @var IDBConnection */ private $connection; /** * @param \OC\Files\Storage\Storage $storage * @param IDBConnection $connection */ public function __construct(\OC\Files\Storage\Storage $storage, IDBConnection $connection) { $this->storage = $storage; $this->connection = $connection; } /** * @param string $internalPath * @param int $time * @param int $sizeDifference number of bytes the file has grown */ public function propagateChange($internalPath, $time, $sizeDifference = 0) { $storageId = (int)$this->storage->getStorageCache()->getNumericId(); $parents = $this->getParents($internalPath); if ($this->inBatch) { foreach ($parents as $parent) { $this->addToBatch($parent, $time, $sizeDifference); } return; } $parentHashes = array_map('md5', $parents); $etag = uniqid(); // since we give all folders the same etag we don't ask the storage for the etag $builder = $this->connection->getQueryBuilder(); $hashParams = array_map(function ($hash) use ($builder) { return $builder->expr()->literal($hash); }, $parentHashes); $builder->update('filecache') ->set('mtime', $builder->createFunction('GREATEST(`mtime`, ' . $builder->createNamedParameter((int)$time, IQueryBuilder::PARAM_INT) . ')')) ->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR)) ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) ->andWhere($builder->expr()->in('path_hash', $hashParams)); $builder->execute(); if ($sizeDifference !== 0) { // we need to do size separably so we can ignore entries with uncalculated size $builder = $this->connection->getQueryBuilder(); $builder->update('filecache') ->set('size', $builder->createFunction('`size` + ' . $builder->createNamedParameter($sizeDifference))) ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) ->andWhere($builder->expr()->in('path_hash', $hashParams)) ->andWhere($builder->expr()->gt('size', $builder->expr()->literal(-1, IQueryBuilder::PARAM_INT))); } $builder->execute(); } protected function getParents($path) { $parts = explode('/', $path); $parent = ''; $parents = []; foreach ($parts as $part) { $parents[] = $parent; $parent = trim($parent . '/' . $part, '/'); } return $parents; } /** * Mark the beginning of a propagation batch * * Note that not all cache setups support propagation in which case this will be a noop * * Batching for cache setups that do support it has to be explicit since the cache state is not fully consistent * before the batch is committed. */ public function beginBatch() { $this->inBatch = true; } private function addToBatch($internalPath, $time, $sizeDifference) { if (!isset($this->batch[$internalPath])) { $this->batch[$internalPath] = [ 'hash' => md5($internalPath), 'time' => $time, 'size' => $sizeDifference ]; } else { $this->batch[$internalPath]['size'] += $sizeDifference; if ($time > $this->batch[$internalPath]['time']) { $this->batch[$internalPath]['time'] = $time; } } } /** * Commit the active propagation batch */ public function commitBatch() { if (!$this->inBatch) { throw new \BadMethodCallException('Not in batch'); } $this->inBatch = false; $this->connection->beginTransaction(); $query = $this->connection->getQueryBuilder(); $storageId = (int)$this->storage->getStorageCache()->getNumericId(); $query->update('filecache') ->set('mtime', $query->createFunction('GREATEST(`mtime`, ' . $query->createParameter('time') . ')')) ->set('etag', $query->expr()->literal(uniqid())) ->where($query->expr()->eq('storage', $query->expr()->literal($storageId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('path_hash', $query->createParameter('hash'))); $sizeQuery = $this->connection->getQueryBuilder(); $sizeQuery->update('filecache') ->set('size', $sizeQuery->createFunction('`size` + ' . $sizeQuery->createParameter('size'))) ->where($query->expr()->eq('storage', $query->expr()->literal($storageId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('path_hash', $query->createParameter('hash'))) ->andWhere($sizeQuery->expr()->gt('size', $sizeQuery->expr()->literal(-1, IQueryBuilder::PARAM_INT))); foreach ($this->batch as $item) { $query->setParameter('time', $item['time'], IQueryBuilder::PARAM_INT); $query->setParameter('hash', $item['hash']); $query->execute(); if ($item['size']) { $sizeQuery->setParameter('size', $item['size'], IQueryBuilder::PARAM_INT); $sizeQuery->setParameter('hash', $item['hash']); $sizeQuery->execute(); } } $this->batch = []; $this->connection->commit(); } } private/Files/Cache/StorageGlobal.php 0000604 00000004356 15247130452 0013530 0 ustar 00 <?php /** * @copyright Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; /** * Handle the mapping between the string and numeric storage ids * * Each storage has 2 different ids * a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share') * and a numeric storage id which is referenced in the file cache * * A mapping between the two storage ids is stored in the database and accessible trough this class * * @package OC\Files\Cache */ class StorageGlobal { /** @var IDBConnection */ private $connection; /** @var array[] */ private $cache = []; public function __construct(IDBConnection $connection) { $this->connection = $connection; } /** * @param string[] $storageIds */ public function loadForStorageIds(array $storageIds) { $builder = $this->connection->getQueryBuilder(); $query = $builder->select(['id', 'numeric_id', 'available', 'last_checked']) ->from('storages') ->where($builder->expr()->in('id', $builder->createNamedParameter(array_values($storageIds), IQueryBuilder::PARAM_STR_ARRAY))); $result = $query->execute(); while ($row = $result->fetch()) { $this->cache[$row['id']] = $row; } } /** * @param string $storageId * @return array|null */ public function getStorageInfo($storageId) { if (!isset($this->cache[$storageId])) { $this->loadForStorageIds([$storageId]); } return isset($this->cache[$storageId]) ? $this->cache[$storageId] : null; } public function clearCache() { $this->cache = []; } } private/Files/Cache/Updater.php 0000604 00000015061 15247130452 0012402 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Daniel Jagszent <daniel@jagszent.de> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Cache\IUpdater; use OCP\Files\Storage\IStorage; /** * Update the cache and propagate changes * */ class Updater implements IUpdater { /** * @var bool */ protected $enabled = true; /** * @var \OC\Files\Storage\Storage */ protected $storage; /** * @var \OC\Files\Cache\Propagator */ protected $propagator; /** * @var Scanner */ protected $scanner; /** * @var Cache */ protected $cache; /** * @param \OC\Files\Storage\Storage $storage */ public function __construct(\OC\Files\Storage\Storage $storage) { $this->storage = $storage; $this->propagator = $storage->getPropagator(); $this->scanner = $storage->getScanner(); $this->cache = $storage->getCache(); } /** * Disable updating the cache trough this updater */ public function disable() { $this->enabled = false; } /** * Re-enable the updating of the cache trough this updater */ public function enable() { $this->enabled = true; } /** * Get the propagator for etags and mtime for the view the updater works on * * @return Propagator */ public function getPropagator() { return $this->propagator; } /** * Propagate etag and mtime changes for the parent folders of $path up to the root of the filesystem * * @param string $path the path of the file to propagate the changes for * @param int|null $time the timestamp to set as mtime for the parent folders, if left out the current time is used */ public function propagate($path, $time = null) { if (Scanner::isPartialFile($path)) { return; } $this->propagator->propagateChange($path, $time); } /** * Update the cache for $path and update the size, etag and mtime of the parent folders * * @param string $path * @param int $time */ public function update($path, $time = null) { if (!$this->enabled or Scanner::isPartialFile($path)) { return; } if (is_null($time)) { $time = time(); } $data = $this->scanner->scan($path, Scanner::SCAN_SHALLOW, -1, false); if ( isset($data['oldSize']) && isset($data['size']) && !$data['encrypted'] // encryption is a pita and touches the cache itself ) { $sizeDifference = $data['size'] - $data['oldSize']; } else { // scanner didn't provide size info, fallback to full size calculation $sizeDifference = 0; if ($this->cache instanceof Cache) { $this->cache->correctFolderSize($path, $data); } } $this->correctParentStorageMtime($path); $this->propagator->propagateChange($path, $time, $sizeDifference); } /** * Remove $path from the cache and update the size, etag and mtime of the parent folders * * @param string $path */ public function remove($path) { if (!$this->enabled or Scanner::isPartialFile($path)) { return; } $parent = dirname($path); if ($parent === '.') { $parent = ''; } $entry = $this->cache->get($path); $this->cache->remove($path); $this->correctParentStorageMtime($path); if ($entry instanceof ICacheEntry) { $this->propagator->propagateChange($path, time(), -$entry->getSize()); } else { $this->propagator->propagateChange($path, time()); if ($this->cache instanceof Cache) { $this->cache->correctFolderSize($parent); } } } /** * Rename a file or folder in the cache and update the size, etag and mtime of the parent folders * * @param IStorage $sourceStorage * @param string $source * @param string $target */ public function renameFromStorage(IStorage $sourceStorage, $source, $target) { if (!$this->enabled or Scanner::isPartialFile($source) or Scanner::isPartialFile($target)) { return; } $time = time(); $sourceCache = $sourceStorage->getCache(); $sourceUpdater = $sourceStorage->getUpdater(); $sourcePropagator = $sourceStorage->getPropagator(); if ($sourceCache->inCache($source)) { if ($this->cache->inCache($target)) { $this->cache->remove($target); } if ($sourceStorage === $this->storage) { $this->cache->move($source, $target); } else { $this->cache->moveFromCache($sourceCache, $source, $target); } } if (pathinfo($source, PATHINFO_EXTENSION) !== pathinfo($target, PATHINFO_EXTENSION)) { // handle mime type change $mimeType = $this->storage->getMimeType($target); $fileId = $this->cache->getId($target); $this->cache->update($fileId, ['mimetype' => $mimeType]); } if ($sourceCache instanceof Cache) { $sourceCache->correctFolderSize($source); } if ($this->cache instanceof Cache) { $this->cache->correctFolderSize($target); } if ($sourceUpdater instanceof Updater) { $sourceUpdater->correctParentStorageMtime($source); } $this->correctParentStorageMtime($target); $this->updateStorageMTimeOnly($target); $sourcePropagator->propagateChange($source, $time); $this->propagator->propagateChange($target, $time); } private function updateStorageMTimeOnly($internalPath) { $fileId = $this->cache->getId($internalPath); if ($fileId !== -1) { $this->cache->update( $fileId, [ 'mtime' => null, // this magic tells it to not overwrite mtime 'storage_mtime' => $this->storage->filemtime($internalPath) ] ); } } /** * update the storage_mtime of the direct parent in the cache to the mtime from the storage * * @param string $internalPath */ private function correctParentStorageMtime($internalPath) { $parentId = $this->cache->getParentId($internalPath); $parent = dirname($internalPath); if ($parentId != -1) { $mtime = $this->storage->filemtime($parent); if ($mtime !== false) { $this->cache->update($parentId, array('storage_mtime' => $mtime)); } } } } private/Files/Cache/FailedCache.php 0000604 00000005407 15247130452 0013111 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\Constants; use OCP\Files\Cache\ICache; use OCP\Files\Search\ISearchQuery; /** * Storage placeholder to represent a missing precondition, storage unavailable */ class FailedCache implements ICache { /** @var bool whether to show the failed storage in the ui */ private $visible; /** * FailedCache constructor. * * @param bool $visible */ public function __construct($visible = true) { $this->visible = $visible; } public function getNumericStorageId() { return -1; } public function get($file) { if ($file === '') { return new CacheEntry([ 'fileid' => -1, 'size' => 0, 'mimetype' => 'httpd/unix-directory', 'mimepart' => 'httpd', 'permissions' => $this->visible ? Constants::PERMISSION_READ : 0, 'mtime' => time() ]); } else { return false; } } public function getFolderContents($folder) { return []; } public function getFolderContentsById($fileId) { return []; } public function put($file, array $data) { return; } public function insert($file, array $data) { return; } public function update($id, array $data) { return; } public function getId($file) { return -1; } public function getParentId($file) { return -1; } public function inCache($file) { return false; } public function remove($file) { return; } public function move($source, $target) { return; } public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { return; } public function clear() { return; } public function getStatus($file) { return ICache::NOT_FOUND; } public function search($pattern) { return []; } public function searchByMime($mimetype) { return []; } public function searchByTag($tag, $userId) { return []; } public function searchQuery(ISearchQuery $query) { return []; } public function getAll() { return []; } public function getIncomplete() { return []; } public function getPathById($id) { return null; } public function normalize($path) { return $path; } } private/Files/Cache/HomePropagator.php 0000604 00000003016 15247130452 0013722 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; use OCP\IDBConnection; class HomePropagator extends Propagator { private $ignoredBaseFolders; /** * @param \OC\Files\Storage\Storage $storage */ public function __construct(\OC\Files\Storage\Storage $storage, IDBConnection $connection) { parent::__construct($storage, $connection); $this->ignoredBaseFolders = ['files_encryption']; } /** * @param string $internalPath * @param int $time * @param int $sizeDifference number of bytes the file has grown */ public function propagateChange($internalPath, $time, $sizeDifference = 0) { list($baseFolder) = explode('/', $internalPath, 2); if (in_array($baseFolder, $this->ignoredBaseFolders)) { return []; } else { parent::propagateChange($internalPath, $time, $sizeDifference); } } } private/Files/Cache/Storage.php 0000604 00000013207 15247130452 0012402 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Cache; /** * Handle the mapping between the string and numeric storage ids * * Each storage has 2 different ids * a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share') * and a numeric storage id which is referenced in the file cache * * A mapping between the two storage ids is stored in the database and accessible trough this class * * @package OC\Files\Cache */ class Storage { /** @var StorageGlobal|null */ private static $globalCache = null; private $storageId; private $numericId; /** * @return StorageGlobal */ public static function getGlobalCache() { if (is_null(self::$globalCache)) { self::$globalCache = new StorageGlobal(\OC::$server->getDatabaseConnection()); } return self::$globalCache; } /** * @param \OC\Files\Storage\Storage|string $storage * @param bool $isAvailable * @throws \RuntimeException */ public function __construct($storage, $isAvailable = true) { if ($storage instanceof \OC\Files\Storage\Storage) { $this->storageId = $storage->getId(); } else { $this->storageId = $storage; } $this->storageId = self::adjustStorageId($this->storageId); if ($row = self::getStorageById($this->storageId)) { $this->numericId = (int)$row['numeric_id']; } else { $connection = \OC::$server->getDatabaseConnection(); $available = $isAvailable ? 1 : 0; if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $available])) { $this->numericId = (int)$connection->lastInsertId('*PREFIX*storages'); } else { if ($row = self::getStorageById($this->storageId)) { $this->numericId = (int)$row['numeric_id']; } else { throw new \RuntimeException('Storage could neither be inserted nor be selected from the database'); } } } } /** * @param string $storageId * @return array */ public static function getStorageById($storageId) { return self::getGlobalCache()->getStorageInfo($storageId); } /** * Adjusts the storage id to use md5 if too long * @param string $storageId storage id * @return string unchanged $storageId if its length is less than 64 characters, * else returns the md5 of $storageId */ public static function adjustStorageId($storageId) { if (strlen($storageId) > 64) { return md5($storageId); } return $storageId; } /** * Get the numeric id for the storage * * @return int */ public function getNumericId() { return $this->numericId; } /** * Get the string id for the storage * * @param int $numericId * @return string|null either the storage id string or null if the numeric id is not known */ public static function getStorageId($numericId) { $sql = 'SELECT `id` FROM `*PREFIX*storages` WHERE `numeric_id` = ?'; $result = \OC_DB::executeAudited($sql, array($numericId)); if ($row = $result->fetchRow()) { return $row['id']; } else { return null; } } /** * Get the numeric of the storage with the provided string id * * @param $storageId * @return int|null either the numeric storage id or null if the storage id is not knwon */ public static function getNumericStorageId($storageId) { $storageId = self::adjustStorageId($storageId); if ($row = self::getStorageById($storageId)) { return (int)$row['numeric_id']; } else { return null; } } /** * @return array|null [ available, last_checked ] */ public function getAvailability() { if ($row = self::getStorageById($this->storageId)) { return [ 'available' => ((int)$row['available'] === 1), 'last_checked' => $row['last_checked'] ]; } else { return null; } } /** * @param bool $isAvailable */ public function setAvailability($isAvailable) { $sql = 'UPDATE `*PREFIX*storages` SET `available` = ?, `last_checked` = ? WHERE `id` = ?'; $available = $isAvailable ? 1 : 0; \OC_DB::executeAudited($sql, array($available, time(), $this->storageId)); } /** * Check if a string storage id is known * * @param string $storageId * @return bool */ public static function exists($storageId) { return !is_null(self::getNumericStorageId($storageId)); } /** * remove the entry for the storage * * @param string $storageId */ public static function remove($storageId) { $storageId = self::adjustStorageId($storageId); $numericId = self::getNumericStorageId($storageId); $sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?'; \OC_DB::executeAudited($sql, array($storageId)); if (!is_null($numericId)) { $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?'; \OC_DB::executeAudited($sql, array($numericId)); } } } private/Files/View.php 0000604 00000174563 15247130452 0010722 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author cmeh <cmeh@users.noreply.github.com> * @author Florin Peter <github@florin-peter.de> * @author Jesús Macias <jmacias@solidgear.es> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author karakayasemi <karakayasemi@itu.edu.tr> * @author Klaas Freitag <freitag@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Luke Policinski <lpolicinski@gmail.com> * @author Martin Mattel <martin.mattel@diemattels.at> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Petr Svoboda <weits666@gmail.com> * @author Piotr Filiciak <piotr@filiciak.pl> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sam Tuke <mail@samtuke.com> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files; use Icewind\Streams\CallbackWrapper; use OC\Files\Mount\MoveableMount; use OC\Files\Storage\Storage; use OC\User\User; use OCP\Constants; use OCP\Files\Cache\ICacheEntry; use OCP\Files\EmptyFileNameException; use OCP\Files\FileNameTooLongException; use OCP\Files\InvalidCharacterInPathException; use OCP\Files\InvalidDirectoryException; use OCP\Files\InvalidPathException; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\Files\ReservedWordException; use OCP\IUser; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; /** * Class to provide access to ownCloud filesystem via a "view", and methods for * working with files within that view (e.g. read, write, delete, etc.). Each * view is restricted to a set of directories via a virtual root. The default view * uses the currently logged in user's data directory as root (parts of * OC_Filesystem are merely a wrapper for OC\Files\View). * * Apps that need to access files outside of the user data folders (to modify files * belonging to a user other than the one currently logged in, for example) should * use this class directly rather than using OC_Filesystem, or making use of PHP's * built-in file manipulation functions. This will ensure all hooks and proxies * are triggered correctly. * * Filesystem functions are not called directly; they are passed to the correct * \OC\Files\Storage\Storage object */ class View { /** @var string */ private $fakeRoot = ''; /** * @var \OCP\Lock\ILockingProvider */ protected $lockingProvider; private $lockingEnabled; private $updaterEnabled = true; /** @var \OC\User\Manager */ private $userManager; /** @var \OCP\ILogger */ private $logger; /** * @param string $root * @throws \Exception If $root contains an invalid path */ public function __construct($root = '') { if (is_null($root)) { throw new \InvalidArgumentException('Root can\'t be null'); } if (!Filesystem::isValidPath($root)) { throw new \Exception(); } $this->fakeRoot = $root; $this->lockingProvider = \OC::$server->getLockingProvider(); $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); $this->userManager = \OC::$server->getUserManager(); $this->logger = \OC::$server->getLogger(); } public function getAbsolutePath($path = '/') { if ($path === null) { return null; } $this->assertPathLength($path); if ($path === '') { $path = '/'; } if ($path[0] !== '/') { $path = '/' . $path; } return $this->fakeRoot . $path; } /** * change the root to a fake root * * @param string $fakeRoot * @return boolean|null */ public function chroot($fakeRoot) { if (!$fakeRoot == '') { if ($fakeRoot[0] !== '/') { $fakeRoot = '/' . $fakeRoot; } } $this->fakeRoot = $fakeRoot; } /** * get the fake root * * @return string */ public function getRoot() { return $this->fakeRoot; } /** * get path relative to the root of the view * * @param string $path * @return string */ public function getRelativePath($path) { $this->assertPathLength($path); if ($this->fakeRoot == '') { return $path; } if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) { return '/'; } // missing slashes can cause wrong matches! $root = rtrim($this->fakeRoot, '/') . '/'; if (strpos($path, $root) !== 0) { return null; } else { $path = substr($path, strlen($this->fakeRoot)); if (strlen($path) === 0) { return '/'; } else { return $path; } } } /** * get the mountpoint of the storage object for a path * ( note: because a storage is not always mounted inside the fakeroot, the * returned mountpoint is relative to the absolute root of the filesystem * and does not take the chroot into account ) * * @param string $path * @return string */ public function getMountPoint($path) { return Filesystem::getMountPoint($this->getAbsolutePath($path)); } /** * get the mountpoint of the storage object for a path * ( note: because a storage is not always mounted inside the fakeroot, the * returned mountpoint is relative to the absolute root of the filesystem * and does not take the chroot into account ) * * @param string $path * @return \OCP\Files\Mount\IMountPoint */ public function getMount($path) { return Filesystem::getMountManager()->find($this->getAbsolutePath($path)); } /** * resolve a path to a storage and internal path * * @param string $path * @return array an array consisting of the storage and the internal path */ public function resolvePath($path) { $a = $this->getAbsolutePath($path); $p = Filesystem::normalizePath($a); return Filesystem::resolvePath($p); } /** * return the path to a local version of the file * we need this because we can't know if a file is stored local or not from * outside the filestorage and for some purposes a local file is needed * * @param string $path * @return string */ public function getLocalFile($path) { $parent = substr($path, 0, strrpos($path, '/')); $path = $this->getAbsolutePath($path); list($storage, $internalPath) = Filesystem::resolvePath($path); if (Filesystem::isValidPath($parent) and $storage) { return $storage->getLocalFile($internalPath); } else { return null; } } /** * @param string $path * @return string */ public function getLocalFolder($path) { $parent = substr($path, 0, strrpos($path, '/')); $path = $this->getAbsolutePath($path); list($storage, $internalPath) = Filesystem::resolvePath($path); if (Filesystem::isValidPath($parent) and $storage) { return $storage->getLocalFolder($internalPath); } else { return null; } } /** * the following functions operate with arguments and return values identical * to those of their PHP built-in equivalents. Mostly they are merely wrappers * for \OC\Files\Storage\Storage via basicOperation(). */ public function mkdir($path) { return $this->basicOperation('mkdir', $path, array('create', 'write')); } /** * remove mount point * * @param \OC\Files\Mount\MoveableMount $mount * @param string $path relative to data/ * @return boolean */ protected function removeMount($mount, $path) { if ($mount instanceof MoveableMount) { // cut of /user/files to get the relative path to data/user/files $pathParts = explode('/', $path, 4); $relPath = '/' . $pathParts[3]; $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true); \OC_Hook::emit( Filesystem::CLASSNAME, "umount", array(Filesystem::signal_param_path => $relPath) ); $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true); $result = $mount->removeMount(); $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true); if ($result) { \OC_Hook::emit( Filesystem::CLASSNAME, "post_umount", array(Filesystem::signal_param_path => $relPath) ); } $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true); return $result; } else { // do not allow deleting the storage's root / the mount point // because for some storages it might delete the whole contents // but isn't supposed to work that way return false; } } public function disableCacheUpdate() { $this->updaterEnabled = false; } public function enableCacheUpdate() { $this->updaterEnabled = true; } protected function writeUpdate(Storage $storage, $internalPath, $time = null) { if ($this->updaterEnabled) { if (is_null($time)) { $time = time(); } $storage->getUpdater()->update($internalPath, $time); } } protected function removeUpdate(Storage $storage, $internalPath) { if ($this->updaterEnabled) { $storage->getUpdater()->remove($internalPath); } } protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) { if ($this->updaterEnabled) { $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } } /** * @param string $path * @return bool|mixed */ public function rmdir($path) { $absolutePath = $this->getAbsolutePath($path); $mount = Filesystem::getMountManager()->find($absolutePath); if ($mount->getInternalPath($absolutePath) === '') { return $this->removeMount($mount, $absolutePath); } if ($this->is_dir($path)) { $result = $this->basicOperation('rmdir', $path, array('delete')); } else { $result = false; } if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($absolutePath); $storage->getUpdater()->remove($internalPath); } return $result; } /** * @param string $path * @return resource */ public function opendir($path) { return $this->basicOperation('opendir', $path, array('read')); } /** * @param $handle * @return mixed */ public function readdir($handle) { $fsLocal = new Storage\Local(array('datadir' => '/')); return $fsLocal->readdir($handle); } /** * @param string $path * @return bool|mixed */ public function is_dir($path) { if ($path == '/') { return true; } return $this->basicOperation('is_dir', $path); } /** * @param string $path * @return bool|mixed */ public function is_file($path) { if ($path == '/') { return false; } return $this->basicOperation('is_file', $path); } /** * @param string $path * @return mixed */ public function stat($path) { return $this->basicOperation('stat', $path); } /** * @param string $path * @return mixed */ public function filetype($path) { return $this->basicOperation('filetype', $path); } /** * @param string $path * @return mixed */ public function filesize($path) { return $this->basicOperation('filesize', $path); } /** * @param string $path * @return bool|mixed * @throws \OCP\Files\InvalidPathException */ public function readfile($path) { $this->assertPathLength($path); @ob_end_clean(); $handle = $this->fopen($path, 'rb'); if ($handle) { $chunkSize = 8192; // 8 kB chunks while (!feof($handle)) { echo fread($handle, $chunkSize); flush(); } fclose($handle); $size = $this->filesize($path); return $size; } return false; } /** * @param string $path * @param int $from * @param int $to * @return bool|mixed * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\UnseekableException */ public function readfilePart($path, $from, $to) { $this->assertPathLength($path); @ob_end_clean(); $handle = $this->fopen($path, 'rb'); if ($handle) { if (fseek($handle, $from) === 0) { $chunkSize = 8192; // 8 kB chunks $end = $to + 1; while (!feof($handle) && ftell($handle) < $end) { $len = $end - ftell($handle); if ($len > $chunkSize) { $len = $chunkSize; } echo fread($handle, $len); flush(); } $size = ftell($handle) - $from; return $size; } throw new \OCP\Files\UnseekableException('fseek error'); } return false; } /** * @param string $path * @return mixed */ public function isCreatable($path) { return $this->basicOperation('isCreatable', $path); } /** * @param string $path * @return mixed */ public function isReadable($path) { return $this->basicOperation('isReadable', $path); } /** * @param string $path * @return mixed */ public function isUpdatable($path) { return $this->basicOperation('isUpdatable', $path); } /** * @param string $path * @return bool|mixed */ public function isDeletable($path) { $absolutePath = $this->getAbsolutePath($path); $mount = Filesystem::getMountManager()->find($absolutePath); if ($mount->getInternalPath($absolutePath) === '') { return $mount instanceof MoveableMount; } return $this->basicOperation('isDeletable', $path); } /** * @param string $path * @return mixed */ public function isSharable($path) { return $this->basicOperation('isSharable', $path); } /** * @param string $path * @return bool|mixed */ public function file_exists($path) { if ($path == '/') { return true; } return $this->basicOperation('file_exists', $path); } /** * @param string $path * @return mixed */ public function filemtime($path) { return $this->basicOperation('filemtime', $path); } /** * @param string $path * @param int|string $mtime * @return bool */ public function touch($path, $mtime = null) { if (!is_null($mtime) and !is_numeric($mtime)) { $mtime = strtotime($mtime); } $hooks = array('touch'); if (!$this->file_exists($path)) { $hooks[] = 'create'; $hooks[] = 'write'; } $result = $this->basicOperation('touch', $path, $hooks, $mtime); if (!$result) { // If create file fails because of permissions on external storage like SMB folders, // check file exists and return false if not. if (!$this->file_exists($path)) { return false; } if (is_null($mtime)) { $mtime = time(); } //if native touch fails, we emulate it by changing the mtime in the cache $this->putFileInfo($path, array('mtime' => floor($mtime))); } return true; } /** * @param string $path * @return mixed */ public function file_get_contents($path) { return $this->basicOperation('file_get_contents', $path, array('read')); } /** * @param bool $exists * @param string $path * @param bool $run */ protected function emit_file_hooks_pre($exists, $path, &$run) { if (!$exists) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array( Filesystem::signal_param_path => $this->getHookPath($path), Filesystem::signal_param_run => &$run, )); } else { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array( Filesystem::signal_param_path => $this->getHookPath($path), Filesystem::signal_param_run => &$run, )); } \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array( Filesystem::signal_param_path => $this->getHookPath($path), Filesystem::signal_param_run => &$run, )); } /** * @param bool $exists * @param string $path */ protected function emit_file_hooks_post($exists, $path) { if (!$exists) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array( Filesystem::signal_param_path => $this->getHookPath($path), )); } else { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array( Filesystem::signal_param_path => $this->getHookPath($path), )); } \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array( Filesystem::signal_param_path => $this->getHookPath($path), )); } /** * @param string $path * @param mixed $data * @return bool|mixed * @throws \Exception */ public function file_put_contents($path, $data) { if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path) ) { $path = $this->getRelativePath($absolutePath); $this->lockFile($path, ILockingProvider::LOCK_SHARED); $exists = $this->file_exists($path); $run = true; if ($this->shouldEmitHooks($path)) { $this->emit_file_hooks_pre($exists, $path, $run); } if (!$run) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); return false; } $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = $this->resolvePath($path); $target = $storage->fopen($internalPath, 'w'); if ($target) { list (, $result) = \OC_Helper::streamCopy($data, $target); fclose($target); fclose($data); $this->writeUpdate($storage, $internalPath); $this->changeLock($path, ILockingProvider::LOCK_SHARED); if ($this->shouldEmitHooks($path) && $result !== false) { $this->emit_file_hooks_post($exists, $path); } $this->unlockFile($path, ILockingProvider::LOCK_SHARED); return $result; } else { $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); return false; } } else { return false; } } else { $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write'); return $this->basicOperation('file_put_contents', $path, $hooks, $data); } } /** * @param string $path * @return bool|mixed */ public function unlink($path) { if ($path === '' || $path === '/') { // do not allow deleting the root return false; } $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); $mount = Filesystem::getMountManager()->find($absolutePath . $postFix); if ($mount and $mount->getInternalPath($absolutePath) === '') { return $this->removeMount($mount, $absolutePath); } if ($this->is_dir($path)) { $result = $this->basicOperation('rmdir', $path, ['delete']); } else { $result = $this->basicOperation('unlink', $path, ['delete']); } if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($absolutePath); $storage->getUpdater()->remove($internalPath); return true; } else { return $result; } } /** * @param string $directory * @return bool|mixed */ public function deleteAll($directory) { return $this->rmdir($directory); } /** * Rename/move a file or folder from the source path to target path. * * @param string $path1 source path * @param string $path2 target path * * @return bool|mixed */ public function rename($path1, $path2) { $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); $result = false; if ( Filesystem::isValidPath($path2) and Filesystem::isValidPath($path1) and !Filesystem::isFileBlacklisted($path2) ) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); $exists = $this->file_exists($path2); if ($path1 == null or $path2 == null) { return false; } $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true); try { $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true); } catch (LockedException $e) { $this->unlockFile($path1, ILockingProvider::LOCK_SHARED); throw $e; } $run = true; if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) { // if it was a rename from a part file to a regular file it was a write and not a rename operation $this->emit_file_hooks_pre($exists, $path2, $run); } elseif ($this->shouldEmitHooks($path1)) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_rename, array( Filesystem::signal_param_oldpath => $this->getHookPath($path1), Filesystem::signal_param_newpath => $this->getHookPath($path2), Filesystem::signal_param_run => &$run ) ); } if ($run) { $this->verifyPath(dirname($path2), basename($path2)); $manager = Filesystem::getMountManager(); $mount1 = $this->getMount($path1); $mount2 = $this->getMount($path2); $storage1 = $mount1->getStorage(); $storage2 = $mount2->getStorage(); $internalPath1 = $mount1->getInternalPath($absolutePath1); $internalPath2 = $mount2->getInternalPath($absolutePath2); $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true); $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true); if ($internalPath1 === '') { if ($mount1 instanceof MoveableMount) { if ($this->isTargetAllowed($absolutePath2)) { /** * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 */ $sourceMountPoint = $mount1->getMountPoint(); $result = $mount1->moveMount($absolutePath2); $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); } else { $result = false; } } else { $result = false; } // moving a file/folder within the same mount point } elseif ($storage1 === $storage2) { if ($storage1) { $result = $storage1->rename($internalPath1, $internalPath2); } else { $result = false; } // moving a file/folder between storages (from $storage1 to $storage2) } else { $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); } if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { // if it was a rename from a part file to a regular file it was a write and not a rename operation $this->writeUpdate($storage2, $internalPath2); } else if ($result) { if ($internalPath1 !== '') { // don't do a cache update for moved mounts $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); } } $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true); $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true); if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { if ($this->shouldEmitHooks()) { $this->emit_file_hooks_post($exists, $path2); } } elseif ($result) { if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_post_rename, array( Filesystem::signal_param_oldpath => $this->getHookPath($path1), Filesystem::signal_param_newpath => $this->getHookPath($path2) ) ); } } } $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true); $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true); } return $result; } /** * Copy a file/folder from the source path to target path * * @param string $path1 source path * @param string $path2 target path * @param bool $preserveMtime whether to preserve mtime on the copy * * @return bool|mixed */ public function copy($path1, $path2, $preserveMtime = false) { $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); $result = false; if ( Filesystem::isValidPath($path2) and Filesystem::isValidPath($path1) and !Filesystem::isFileBlacklisted($path2) ) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); if ($path1 == null or $path2 == null) { return false; } $run = true; $this->lockFile($path2, ILockingProvider::LOCK_SHARED); $this->lockFile($path1, ILockingProvider::LOCK_SHARED); $lockTypePath1 = ILockingProvider::LOCK_SHARED; $lockTypePath2 = ILockingProvider::LOCK_SHARED; try { $exists = $this->file_exists($path2); if ($this->shouldEmitHooks()) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_copy, array( Filesystem::signal_param_oldpath => $this->getHookPath($path1), Filesystem::signal_param_newpath => $this->getHookPath($path2), Filesystem::signal_param_run => &$run ) ); $this->emit_file_hooks_pre($exists, $path2, $run); } if ($run) { $mount1 = $this->getMount($path1); $mount2 = $this->getMount($path2); $storage1 = $mount1->getStorage(); $internalPath1 = $mount1->getInternalPath($absolutePath1); $storage2 = $mount2->getStorage(); $internalPath2 = $mount2->getInternalPath($absolutePath2); $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE); $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE; if ($mount1->getMountPoint() == $mount2->getMountPoint()) { if ($storage1) { $result = $storage1->copy($internalPath1, $internalPath2); } else { $result = false; } } else { $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2); } $this->writeUpdate($storage2, $internalPath2); $this->changeLock($path2, ILockingProvider::LOCK_SHARED); $lockTypePath2 = ILockingProvider::LOCK_SHARED; if ($this->shouldEmitHooks() && $result !== false) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_post_copy, array( Filesystem::signal_param_oldpath => $this->getHookPath($path1), Filesystem::signal_param_newpath => $this->getHookPath($path2) ) ); $this->emit_file_hooks_post($exists, $path2); } } } catch (\Exception $e) { $this->unlockFile($path2, $lockTypePath2); $this->unlockFile($path1, $lockTypePath1); throw $e; } $this->unlockFile($path2, $lockTypePath2); $this->unlockFile($path1, $lockTypePath1); } return $result; } /** * @param string $path * @param string $mode 'r' or 'w' * @return resource */ public function fopen($path, $mode) { $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support $hooks = array(); switch ($mode) { case 'r': $hooks[] = 'read'; break; case 'r+': case 'w+': case 'x+': case 'a+': $hooks[] = 'read'; $hooks[] = 'write'; break; case 'w': case 'x': case 'a': $hooks[] = 'write'; break; default: \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR); } if ($mode !== 'r' && $mode !== 'w') { \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends'); } return $this->basicOperation('fopen', $path, $hooks, $mode); } /** * @param string $path * @return bool|string * @throws \OCP\Files\InvalidPathException */ public function toTmpFile($path) { $this->assertPathLength($path); if (Filesystem::isValidPath($path)) { $source = $this->fopen($path, 'r'); if ($source) { $extension = pathinfo($path, PATHINFO_EXTENSION); $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension); file_put_contents($tmpFile, $source); return $tmpFile; } else { return false; } } else { return false; } } /** * @param string $tmpFile * @param string $path * @return bool|mixed * @throws \OCP\Files\InvalidPathException */ public function fromTmpFile($tmpFile, $path) { $this->assertPathLength($path); if (Filesystem::isValidPath($path)) { // Get directory that the file is going into $filePath = dirname($path); // Create the directories if any if (!$this->file_exists($filePath)) { $result = $this->createParentDirectories($filePath); if ($result === false) { return false; } } $source = fopen($tmpFile, 'r'); if ($source) { $result = $this->file_put_contents($path, $source); // $this->file_put_contents() might have already closed // the resource, so we check it, before trying to close it // to avoid messages in the error log. if (is_resource($source)) { fclose($source); } unlink($tmpFile); return $result; } else { return false; } } else { return false; } } /** * @param string $path * @return mixed * @throws \OCP\Files\InvalidPathException */ public function getMimeType($path) { $this->assertPathLength($path); return $this->basicOperation('getMimeType', $path); } /** * @param string $type * @param string $path * @param bool $raw * @return bool|null|string */ public function hash($type, $path, $raw = false) { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; } if ($this->shouldEmitHooks($path)) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_read, array(Filesystem::signal_param_path => $this->getHookPath($path)) ); } list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($storage) { $result = $storage->hash($type, $internalPath, $raw); return $result; } } return null; } /** * @param string $path * @return mixed * @throws \OCP\Files\InvalidPathException */ public function free_space($path = '/') { $this->assertPathLength($path); $result = $this->basicOperation('free_space', $path); if ($result === null) { throw new InvalidPathException(); } return $result; } /** * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage * * @param string $operation * @param string $path * @param array $hooks (optional) * @param mixed $extraParam (optional) * @return mixed * @throws \Exception * * This method takes requests for basic filesystem functions (e.g. reading & writing * files), processes hooks and proxies, sanitises paths, and finally passes them on to * \OC\Files\Storage\Storage for delegation to a storage backend for execution */ private function basicOperation($operation, $path, $hooks = [], $extraParam = null) { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path) ) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; } if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) { // always a shared lock during pre-hooks so the hook can read the file $this->lockFile($path, ILockingProvider::LOCK_SHARED); } $run = $this->runHooks($hooks, $path); /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($run and $storage) { if (in_array('write', $hooks) || in_array('delete', $hooks)) { $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); } try { if (!is_null($extraParam)) { $result = $storage->$operation($internalPath, $extraParam); } else { $result = $storage->$operation($internalPath); } } catch (\Exception $e) { if (in_array('write', $hooks) || in_array('delete', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); } else if (in_array('read', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } throw $e; } if ($result && in_array('delete', $hooks) and $result) { $this->removeUpdate($storage, $internalPath); } if ($result && in_array('write', $hooks) and $operation !== 'fopen') { $this->writeUpdate($storage, $internalPath); } if ($result && in_array('touch', $hooks)) { $this->writeUpdate($storage, $internalPath, $extraParam); } if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { $this->changeLock($path, ILockingProvider::LOCK_SHARED); } $unlockLater = false; if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) { $unlockLater = true; // make sure our unlocking callback will still be called if connection is aborted ignore_user_abort(true); $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) { if (in_array('write', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); } else if (in_array('read', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } }); } if ($this->shouldEmitHooks($path) && $result !== false) { if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open $this->runHooks($hooks, $path, true); } } if (!$unlockLater && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) ) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } return $result; } else { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } } return null; } /** * get the path relative to the default root for hook usage * * @param string $path * @return string */ private function getHookPath($path) { if (!Filesystem::getView()) { return $path; } return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path)); } private function shouldEmitHooks($path = '') { if ($path && Cache\Scanner::isPartialFile($path)) { return false; } if (!Filesystem::$loaded) { return false; } $defaultRoot = Filesystem::getRoot(); if ($defaultRoot === null) { return false; } if ($this->fakeRoot === $defaultRoot) { return true; } $fullPath = $this->getAbsolutePath($path); if ($fullPath === $defaultRoot) { return true; } return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/'); } /** * @param string[] $hooks * @param string $path * @param bool $post * @return bool */ private function runHooks($hooks, $path, $post = false) { $relativePath = $path; $path = $this->getHookPath($path); $prefix = ($post) ? 'post_' : ''; $run = true; if ($this->shouldEmitHooks($relativePath)) { foreach ($hooks as $hook) { if ($hook != 'read') { \OC_Hook::emit( Filesystem::CLASSNAME, $prefix . $hook, array( Filesystem::signal_param_run => &$run, Filesystem::signal_param_path => $path ) ); } elseif (!$post) { \OC_Hook::emit( Filesystem::CLASSNAME, $prefix . $hook, array( Filesystem::signal_param_path => $path ) ); } } } return $run; } /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @return bool */ public function hasUpdated($path, $time) { return $this->basicOperation('hasUpdated', $path, array(), $time); } /** * @param string $ownerId * @return \OC\User\User */ private function getUserObjectForOwner($ownerId) { $owner = $this->userManager->get($ownerId); if ($owner instanceof IUser) { return $owner; } else { return new User($ownerId, null); } } /** * Get file info from cache * * If the file is not in cached it will be scanned * If the file has changed on storage the cache will be updated * * @param \OC\Files\Storage\Storage $storage * @param string $internalPath * @param string $relativePath * @return array|bool */ private function getCacheEntry($storage, $internalPath, $relativePath) { $cache = $storage->getCache($internalPath); $data = $cache->get($internalPath); $watcher = $storage->getWatcher($internalPath); try { // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data if (!$data || $data['size'] === -1) { $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); if (!$storage->file_exists($internalPath)) { $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); return false; } $scanner = $storage->getScanner($internalPath); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); $data = $cache->get($internalPath); $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); $watcher->update($internalPath, $data); $storage->getPropagator()->propagateChange($internalPath, time()); $data = $cache->get($internalPath); $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); } } catch (LockedException $e) { // if the file is locked we just use the old cache info } return $data; } /** * get the filesystem info * * @param string $path * @param boolean|string $includeMountPoints true to add mountpoint sizes, * 'ext' to add only ext storage mount point sizes. Defaults to true. * defaults to true * @return \OC\Files\FileInfo|false False if file does not exist */ public function getFileInfo($path, $includeMountPoints = true) { $this->assertPathLength($path); if (!Filesystem::isValidPath($path)) { return false; } if (Cache\Scanner::isPartialFile($path)) { return $this->getPartFileInfo($path); } $relativePath = $path; $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); $mount = Filesystem::getMountManager()->find($path); $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($path); if ($storage) { $data = $this->getCacheEntry($storage, $internalPath, $relativePath); if (!$data instanceof ICacheEntry) { return false; } if ($mount instanceof MoveableMount && $internalPath === '') { $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; } $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); if ($data and isset($data['fileid'])) { if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { //add the sizes of other mount points to the folder $extOnly = ($includeMountPoints === 'ext'); $mounts = Filesystem::getMountManager()->findIn($path); $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) { $subStorage = $mount->getStorage(); return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage); })); } } return $info; } return false; } /** * get the content of a directory * * @param string $directory path under datadirectory * @param string $mimetype_filter limit returned content to this mimetype or mimepart * @return FileInfo[] */ public function getDirectoryContent($directory, $mimetype_filter = '') { $this->assertPathLength($directory); if (!Filesystem::isValidPath($directory)) { return []; } $path = $this->getAbsolutePath($directory); $path = Filesystem::normalizePath($path); $mount = $this->getMount($directory); $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($path); if ($storage) { $cache = $storage->getCache($internalPath); $user = \OC_User::getUser(); $data = $this->getCacheEntry($storage, $internalPath, $directory); if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) { return []; } $folderId = $data['fileid']; $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter $sharingDisabled = \OCP\Util::isSharingDisabledForUser(); /** * @var \OC\Files\FileInfo[] $files */ $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { if ($sharingDisabled) { $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); }, $contents); //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders $mounts = Filesystem::getMountManager()->findIn($path); $dirLength = strlen($path); foreach ($mounts as $mount) { $mountPoint = $mount->getMountPoint(); $subStorage = $mount->getStorage(); if ($subStorage) { $subCache = $subStorage->getCache(''); $rootEntry = $subCache->get(''); if (!$rootEntry) { $subScanner = $subStorage->getScanner(''); try { $subScanner->scanFile(''); } catch (\OCP\Files\StorageNotAvailableException $e) { continue; } catch (\OCP\Files\StorageInvalidException $e) { continue; } catch (\Exception $e) { // sometimes when the storage is not available it can be any exception \OCP\Util::writeLog( 'core', 'Exception while scanning storage "' . $subStorage->getId() . '": ' . get_class($e) . ': ' . $e->getMessage(), \OCP\Util::ERROR ); continue; } $rootEntry = $subCache->get(''); } if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) { $relativePath = trim(substr($mountPoint, $dirLength), '/'); if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder $entryName = substr($relativePath, 0, $pos); foreach ($files as &$entry) { if ($entry->getName() === $entryName) { $entry->addSubEntry($rootEntry, $mountPoint); } } } else { //mountpoint in this folder, add an entry for it $rootEntry['name'] = $relativePath; $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; $permissions = $rootEntry['permissions']; // do not allow renaming/deleting the mount point if they are not shared files/folders // for shared files/folders we use the permissions given by the owner if ($mount instanceof MoveableMount) { $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; } else { $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)); } //remove any existing entry with the same name foreach ($files as $i => $file) { if ($file['name'] === $rootEntry['name']) { unset($files[$i]); break; } } $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/ // if sharing was disabled for the user we remove the share permissions if (\OCP\Util::isSharingDisabledForUser()) { $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } $owner = $this->getUserObjectForOwner($subStorage->getOwner('')); $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); } } } } if ($mimetype_filter) { $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { if (strpos($mimetype_filter, '/')) { return $file->getMimetype() === $mimetype_filter; } else { return $file->getMimePart() === $mimetype_filter; } }); } return $files; } else { return []; } } /** * change file metadata * * @param string $path * @param array|\OCP\Files\FileInfo $data * @return int * * returns the fileid of the updated file */ public function putFileInfo($path, $data) { $this->assertPathLength($path); if ($data instanceof FileInfo) { $data = $data->getData(); } $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ list($storage, $internalPath) = Filesystem::resolvePath($path); if ($storage) { $cache = $storage->getCache($path); if (!$cache->inCache($internalPath)) { $scanner = $storage->getScanner($internalPath); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } return $cache->put($internalPath, $data); } else { return -1; } } /** * search for files with the name matching $query * * @param string $query * @return FileInfo[] */ public function search($query) { return $this->searchCommon('search', array('%' . $query . '%')); } /** * search for files with the name matching $query * * @param string $query * @return FileInfo[] */ public function searchRaw($query) { return $this->searchCommon('search', array($query)); } /** * search for files by mimetype * * @param string $mimetype * @return FileInfo[] */ public function searchByMime($mimetype) { return $this->searchCommon('searchByMime', array($mimetype)); } /** * search for files by tag * * @param string|int $tag name or tag id * @param string $userId owner of the tags * @return FileInfo[] */ public function searchByTag($tag, $userId) { return $this->searchCommon('searchByTag', array($tag, $userId)); } /** * @param string $method cache method * @param array $args * @return FileInfo[] */ private function searchCommon($method, $args) { $files = array(); $rootLength = strlen($this->fakeRoot); $mount = $this->getMount(''); $mountPoint = $mount->getMountPoint(); $storage = $mount->getStorage(); if ($storage) { $cache = $storage->getCache(''); $results = call_user_func_array(array($cache, $method), $args); foreach ($results as $result) { if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') { $internalPath = $result['path']; $path = $mountPoint . $result['path']; $result['path'] = substr($mountPoint . $result['path'], $rootLength); $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); } } $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot); foreach ($mounts as $mount) { $mountPoint = $mount->getMountPoint(); $storage = $mount->getStorage(); if ($storage) { $cache = $storage->getCache(''); $relativeMountPoint = substr($mountPoint, $rootLength); $results = call_user_func_array(array($cache, $method), $args); if ($results) { foreach ($results as $result) { $internalPath = $result['path']; $result['path'] = rtrim($relativeMountPoint . $result['path'], '/'); $path = rtrim($mountPoint . $internalPath, '/'); $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); } } } } } return $files; } /** * Get the owner for a file or folder * * @param string $path * @return string the user id of the owner * @throws NotFoundException */ public function getOwner($path) { $info = $this->getFileInfo($path); if (!$info) { throw new NotFoundException($path . ' not found while trying to get owner'); } return $info->getOwner()->getUID(); } /** * get the ETag for a file or folder * * @param string $path * @return string */ public function getETag($path) { /** * @var Storage\Storage $storage * @var string $internalPath */ list($storage, $internalPath) = $this->resolvePath($path); if ($storage) { return $storage->getETag($internalPath); } else { return null; } } /** * Get the path of a file by id, relative to the view * * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file * * @param int $id * @throws NotFoundException * @return string */ public function getPath($id) { $id = (int)$id; $manager = Filesystem::getMountManager(); $mounts = $manager->findIn($this->fakeRoot); $mounts[] = $manager->find($this->fakeRoot); // reverse the array so we start with the storage this view is in // which is the most likely to contain the file we're looking for $mounts = array_reverse($mounts); foreach ($mounts as $mount) { /** * @var \OC\Files\Mount\MountPoint $mount */ if ($mount->getStorage()) { $cache = $mount->getStorage()->getCache(); $internalPath = $cache->getPathById($id); if (is_string($internalPath)) { $fullPath = $mount->getMountPoint() . $internalPath; if (!is_null($path = $this->getRelativePath($fullPath))) { return $path; } } } } throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id)); } /** * @param string $path * @throws InvalidPathException */ private function assertPathLength($path) { $maxLen = min(PHP_MAXPATHLEN, 4000); // Check for the string length - performed using isset() instead of strlen() // because isset() is about 5x-40x faster. if (isset($path[$maxLen])) { $pathLen = strlen($path); throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path"); } } /** * check if it is allowed to move a mount point to a given target. * It is not allowed to move a mount point into a different mount point or * into an already shared folder * * @param string $target path * @return boolean */ private function isTargetAllowed($target) { list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target); if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) { \OCP\Util::writeLog('files', 'It is not allowed to move one mount point into another one', \OCP\Util::DEBUG); return false; } // note: cannot use the view because the target is already locked $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath); if ($fileId === -1) { // target might not exist, need to check parent instead $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath)); } // check if any of the parents were shared by the current owner (include collections) $shares = \OCP\Share::getItemShared( 'folder', $fileId, \OCP\Share::FORMAT_NONE, null, true ); if (count($shares) > 0) { \OCP\Util::writeLog('files', 'It is not allowed to move one mount point into a shared folder', \OCP\Util::DEBUG); return false; } return true; } /** * Get a fileinfo object for files that are ignored in the cache (part files) * * @param string $path * @return \OCP\Files\FileInfo */ private function getPartFileInfo($path) { $mount = $this->getMount($path); $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($this->getAbsolutePath($path)); $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); return new FileInfo( $this->getAbsolutePath($path), $storage, $internalPath, [ 'fileid' => null, 'mimetype' => $storage->getMimeType($internalPath), 'name' => basename($path), 'etag' => null, 'size' => $storage->filesize($internalPath), 'mtime' => $storage->filemtime($internalPath), 'encrypted' => false, 'permissions' => \OCP\Constants::PERMISSION_ALL ], $mount, $owner ); } /** * @param string $path * @param string $fileName * @throws InvalidPathException */ public function verifyPath($path, $fileName) { try { /** @type \OCP\Files\Storage $storage */ list($storage, $internalPath) = $this->resolvePath($path); $storage->verifyPath($internalPath, $fileName); } catch (ReservedWordException $ex) { $l = \OC::$server->getL10N('lib'); throw new InvalidPathException($l->t('File name is a reserved word')); } catch (InvalidCharacterInPathException $ex) { $l = \OC::$server->getL10N('lib'); throw new InvalidPathException($l->t('File name contains at least one invalid character')); } catch (FileNameTooLongException $ex) { $l = \OC::$server->getL10N('lib'); throw new InvalidPathException($l->t('File name is too long')); } catch (InvalidDirectoryException $ex) { $l = \OC::$server->getL10N('lib'); throw new InvalidPathException($l->t('Dot files are not allowed')); } catch (EmptyFileNameException $ex) { $l = \OC::$server->getL10N('lib'); throw new InvalidPathException($l->t('Empty filename is not allowed')); } } /** * get all parent folders of $path * * @param string $path * @return string[] */ private function getParents($path) { $path = trim($path, '/'); if (!$path) { return []; } $parts = explode('/', $path); // remove the single file array_pop($parts); $result = array('/'); $resultPath = ''; foreach ($parts as $part) { if ($part) { $resultPath .= '/' . $part; $result[] = $resultPath; } } return $result; } /** * Returns the mount point for which to lock * * @param string $absolutePath absolute path * @param bool $useParentMount true to return parent mount instead of whatever * is mounted directly on the given path, false otherwise * @return \OC\Files\Mount\MountPoint mount point for which to apply locks */ private function getMountForLock($absolutePath, $useParentMount = false) { $results = []; $mount = Filesystem::getMountManager()->find($absolutePath); if (!$mount) { return $results; } if ($useParentMount) { // find out if something is mounted directly on the path $internalPath = $mount->getInternalPath($absolutePath); if ($internalPath === '') { // resolve the parent mount instead $mount = Filesystem::getMountManager()->find(dirname($absolutePath)); } } return $mount; } /** * Lock the given path * * @param string $path the path of the file to lock, relative to the view * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise * @throws \OCP\Lock\LockedException if the path is already locked */ private function lockPath($path, $type, $lockMountPoint = false) { $absolutePath = $this->getAbsolutePath($path); $absolutePath = Filesystem::normalizePath($absolutePath); if (!$this->shouldLockFile($absolutePath)) { return false; } $mount = $this->getMountForLock($absolutePath, $lockMountPoint); if ($mount) { try { $storage = $mount->getStorage(); if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $storage->acquireLock( $mount->getInternalPath($absolutePath), $type, $this->lockingProvider ); } } catch (\OCP\Lock\LockedException $e) { // rethrow with the a human-readable path throw new \OCP\Lock\LockedException( $this->getPathRelativeToFiles($absolutePath), $e ); } } return true; } /** * Change the lock type * * @param string $path the path of the file to lock, relative to the view * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise * @throws \OCP\Lock\LockedException if the path is already locked */ public function changeLock($path, $type, $lockMountPoint = false) { $path = Filesystem::normalizePath($path); $absolutePath = $this->getAbsolutePath($path); $absolutePath = Filesystem::normalizePath($absolutePath); if (!$this->shouldLockFile($absolutePath)) { return false; } $mount = $this->getMountForLock($absolutePath, $lockMountPoint); if ($mount) { try { $storage = $mount->getStorage(); if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $storage->changeLock( $mount->getInternalPath($absolutePath), $type, $this->lockingProvider ); } } catch (\OCP\Lock\LockedException $e) { try { // rethrow with the a human-readable path throw new \OCP\Lock\LockedException( $this->getPathRelativeToFiles($absolutePath), $e ); } catch (\InvalidArgumentException $e) { throw new \OCP\Lock\LockedException( $absolutePath, $e ); } } } return true; } /** * Unlock the given path * * @param string $path the path of the file to unlock, relative to the view * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise */ private function unlockPath($path, $type, $lockMountPoint = false) { $absolutePath = $this->getAbsolutePath($path); $absolutePath = Filesystem::normalizePath($absolutePath); if (!$this->shouldLockFile($absolutePath)) { return false; } $mount = $this->getMountForLock($absolutePath, $lockMountPoint); if ($mount) { $storage = $mount->getStorage(); if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { $storage->releaseLock( $mount->getInternalPath($absolutePath), $type, $this->lockingProvider ); } } return true; } /** * Lock a path and all its parents up to the root of the view * * @param string $path the path of the file to lock relative to the view * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise */ public function lockFile($path, $type, $lockMountPoint = false) { $absolutePath = $this->getAbsolutePath($path); $absolutePath = Filesystem::normalizePath($absolutePath); if (!$this->shouldLockFile($absolutePath)) { return false; } $this->lockPath($path, $type, $lockMountPoint); $parents = $this->getParents($path); foreach ($parents as $parent) { $this->lockPath($parent, ILockingProvider::LOCK_SHARED); } return true; } /** * Unlock a path and all its parents up to the root of the view * * @param string $path the path of the file to lock relative to the view * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise */ public function unlockFile($path, $type, $lockMountPoint = false) { $absolutePath = $this->getAbsolutePath($path); $absolutePath = Filesystem::normalizePath($absolutePath); if (!$this->shouldLockFile($absolutePath)) { return false; } $this->unlockPath($path, $type, $lockMountPoint); $parents = $this->getParents($path); foreach ($parents as $parent) { $this->unlockPath($parent, ILockingProvider::LOCK_SHARED); } return true; } /** * Only lock files in data/user/files/ * * @param string $path Absolute path to the file/folder we try to (un)lock * @return bool */ protected function shouldLockFile($path) { $path = Filesystem::normalizePath($path); $pathSegments = explode('/', $path); if (isset($pathSegments[2])) { // E.g.: /username/files/path-to-file return ($pathSegments[2] === 'files') && (count($pathSegments) > 3); } return strpos($path, '/appdata_') !== 0; } /** * Shortens the given absolute path to be relative to * "$user/files". * * @param string $absolutePath absolute path which is under "files" * * @return string path relative to "files" with trimmed slashes or null * if the path was NOT relative to files * * @throws \InvalidArgumentException if the given path was not under "files" * @since 8.1.0 */ public function getPathRelativeToFiles($absolutePath) { $path = Filesystem::normalizePath($absolutePath); $parts = explode('/', trim($path, '/'), 3); // "$user", "files", "path/to/dir" if (!isset($parts[1]) || $parts[1] !== 'files') { $this->logger->error( '$absolutePath must be relative to "files", value is "%s"', [ $absolutePath ] ); throw new \InvalidArgumentException('$absolutePath must be relative to "files"'); } if (isset($parts[2])) { return $parts[2]; } return ''; } /** * @param string $filename * @return array * @throws \OC\User\NoUserException * @throws NotFoundException */ public function getUidAndFilename($filename) { $info = $this->getFileInfo($filename); if (!$info instanceof \OCP\Files\FileInfo) { throw new NotFoundException($this->getAbsolutePath($filename) . ' not found'); } $uid = $info->getOwner()->getUID(); if ($uid != \OCP\User::getUser()) { Filesystem::initMountPoints($uid); $ownerView = new View('/' . $uid . '/files'); try { $filename = $ownerView->getPath($info['fileid']); } catch (NotFoundException $e) { throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid); } } return [$uid, $filename]; } /** * Creates parent non-existing folders * * @param string $filePath * @return bool */ private function createParentDirectories($filePath) { $directoryParts = explode('/', $filePath); $directoryParts = array_filter($directoryParts); foreach ($directoryParts as $key => $part) { $currentPathElements = array_slice($directoryParts, 0, $key); $currentPath = '/' . implode('/', $currentPathElements); if ($this->is_file($currentPath)) { return false; } if (!$this->file_exists($currentPath)) { $this->mkdir($currentPath); } } return true; } } private/Files/Filesystem.php 0000604 00000057502 15247130452 0012125 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Florin Peter <github@florin-peter.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sam Tuke <mail@samtuke.com> * @author Stephan Peijnik <speijnik@anexia-it.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Class for abstraction of filesystem functions * This class won't call any filesystem functions for itself but will pass them to the correct OC_Filestorage object * this class should also handle all the file permission related stuff * * Hooks provided: * read(path) * write(path, &run) * post_write(path) * create(path, &run) (when a file is created, both create and write will be emitted in that order) * post_create(path) * delete(path, &run) * post_delete(path) * rename(oldpath,newpath, &run) * post_rename(oldpath,newpath) * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emitted in that order) * post_rename(oldpath,newpath) * post_initMountPoints(user, user_dir) * * the &run parameter can be set to false to prevent the operation from occurring */ namespace OC\Files; use OC\Cache\CappedMemoryCache; use OC\Files\Config\MountProviderCollection; use OC\Files\Mount\MountPoint; use OC\Files\Storage\StorageFactory; use OC\Lockdown\Filesystem\NullStorage; use OCP\Files\Config\IMountProvider; use OCP\Files\NotFoundException; use OCP\IUserManager; class Filesystem { /** * @var Mount\Manager $mounts */ private static $mounts; public static $loaded = false; /** * @var \OC\Files\View $defaultInstance */ static private $defaultInstance; static private $usersSetup = array(); static private $normalizedPathCache = null; static private $listeningForProviders = false; /** * classname which used for hooks handling * used as signalclass in OC_Hooks::emit() */ const CLASSNAME = 'OC_Filesystem'; /** * signalname emitted before file renaming * * @param string $oldpath * @param string $newpath */ const signal_rename = 'rename'; /** * signal emitted after file renaming * * @param string $oldpath * @param string $newpath */ const signal_post_rename = 'post_rename'; /** * signal emitted before file/dir creation * * @param string $path * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_create = 'create'; /** * signal emitted after file/dir creation * * @param string $path * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_post_create = 'post_create'; /** * signal emits before file/dir copy * * @param string $oldpath * @param string $newpath * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_copy = 'copy'; /** * signal emits after file/dir copy * * @param string $oldpath * @param string $newpath */ const signal_post_copy = 'post_copy'; /** * signal emits before file/dir save * * @param string $path * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_write = 'write'; /** * signal emits after file/dir save * * @param string $path */ const signal_post_write = 'post_write'; /** * signal emitted before file/dir update * * @param string $path * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_update = 'update'; /** * signal emitted after file/dir update * * @param string $path * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_post_update = 'post_update'; /** * signal emits when reading file/dir * * @param string $path */ const signal_read = 'read'; /** * signal emits when removing file/dir * * @param string $path */ const signal_delete = 'delete'; /** * parameters definitions for signals */ const signal_param_path = 'path'; const signal_param_oldpath = 'oldpath'; const signal_param_newpath = 'newpath'; /** * run - changing this flag to false in hook handler will cancel event */ const signal_param_run = 'run'; const signal_create_mount = 'create_mount'; const signal_delete_mount = 'delete_mount'; const signal_param_mount_type = 'mounttype'; const signal_param_users = 'users'; /** * @var \OC\Files\Storage\StorageFactory $loader */ private static $loader; /** @var bool */ private static $logWarningWhenAddingStorageWrapper = true; /** * @param bool $shouldLog * @return bool previous value * @internal */ public static function logWarningWhenAddingStorageWrapper($shouldLog) { $previousValue = self::$logWarningWhenAddingStorageWrapper; self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog; return $previousValue; } /** * @param string $wrapperName * @param callable $wrapper * @param int $priority */ public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) { if (self::$logWarningWhenAddingStorageWrapper) { \OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [ 'wrapper' => $wrapperName, 'app' => 'filesystem', ]); } $mounts = self::getMountManager()->getAll(); if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) { // do not re-wrap if storage with this name already existed return; } } /** * Returns the storage factory * * @return \OCP\Files\Storage\IStorageFactory */ public static function getLoader() { if (!self::$loader) { self::$loader = new StorageFactory(); } return self::$loader; } /** * Returns the mount manager * * @return \OC\Files\Mount\Manager */ public static function getMountManager($user = '') { if (!self::$mounts) { \OC_Util::setupFS($user); } return self::$mounts; } /** * get the mountpoint of the storage object for a path * ( note: because a storage is not always mounted inside the fakeroot, the * returned mountpoint is relative to the absolute root of the filesystem * and doesn't take the chroot into account ) * * @param string $path * @return string */ static public function getMountPoint($path) { if (!self::$mounts) { \OC_Util::setupFS(); } $mount = self::$mounts->find($path); if ($mount) { return $mount->getMountPoint(); } else { return ''; } } /** * get a list of all mount points in a directory * * @param string $path * @return string[] */ static public function getMountPoints($path) { if (!self::$mounts) { \OC_Util::setupFS(); } $result = array(); $mounts = self::$mounts->findIn($path); foreach ($mounts as $mount) { $result[] = $mount->getMountPoint(); } return $result; } /** * get the storage mounted at $mountPoint * * @param string $mountPoint * @return \OC\Files\Storage\Storage */ public static function getStorage($mountPoint) { if (!self::$mounts) { \OC_Util::setupFS(); } $mount = self::$mounts->find($mountPoint); return $mount->getStorage(); } /** * @param string $id * @return Mount\MountPoint[] */ public static function getMountByStorageId($id) { if (!self::$mounts) { \OC_Util::setupFS(); } return self::$mounts->findByStorageId($id); } /** * @param int $id * @return Mount\MountPoint[] */ public static function getMountByNumericId($id) { if (!self::$mounts) { \OC_Util::setupFS(); } return self::$mounts->findByNumericId($id); } /** * resolve a path to a storage and internal path * * @param string $path * @return array an array consisting of the storage and the internal path */ static public function resolvePath($path) { if (!self::$mounts) { \OC_Util::setupFS(); } $mount = self::$mounts->find($path); if ($mount) { return array($mount->getStorage(), rtrim($mount->getInternalPath($path), '/')); } else { return array(null, null); } } static public function init($user, $root) { if (self::$defaultInstance) { return false; } self::getLoader(); self::$defaultInstance = new View($root); if (!self::$mounts) { self::$mounts = \OC::$server->getMountManager(); } //load custom mount config self::initMountPoints($user); self::$loaded = true; return true; } static public function initMountManager() { if (!self::$mounts) { self::$mounts = \OC::$server->getMountManager(); } } /** * Initialize system and personal mount points for a user * * @param string $user * @throws \OC\User\NoUserException if the user is not available */ public static function initMountPoints($user = '') { if ($user == '') { $user = \OC_User::getUser(); } if ($user === null || $user === false || $user === '') { throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session'); } if (isset(self::$usersSetup[$user])) { return; } self::$usersSetup[$user] = true; $userManager = \OC::$server->getUserManager(); $userObject = $userManager->get($user); if (is_null($userObject)) { \OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR); // reset flag, this will make it possible to rethrow the exception if called again unset(self::$usersSetup[$user]); throw new \OC\User\NoUserException('Backends provided no user object for ' . $user); } $realUid = $userObject->getUID(); // workaround in case of different casings if ($user !== $realUid) { $stack = json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 50)); \OCP\Util::writeLog('files', 'initMountPoints() called with wrong user casing. This could be a bug. Expected: "' . $realUid . '" got "' . $user . '". Stack: ' . $stack, \OCP\Util::WARN); $user = $realUid; // again with the correct casing if (isset(self::$usersSetup[$user])) { return; } self::$usersSetup[$user] = true; } if (\OC::$server->getLockdownManager()->canAccessFilesystem()) { /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */ $mountConfigManager = \OC::$server->getMountProviderCollection(); // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers $homeMount = $mountConfigManager->getHomeMountForUser($userObject); self::getMountManager()->addMount($homeMount); \OC\Files\Filesystem::getStorage($user); // Chance to mount for other storages if ($userObject) { $mounts = $mountConfigManager->addMountForUser($userObject, self::getMountManager()); $mounts[] = $homeMount; $mountConfigManager->registerMounts($userObject, $mounts); } self::listenForNewMountProviders($mountConfigManager, $userManager); } else { self::getMountManager()->addMount(new MountPoint( new NullStorage([]), '/' . $user )); self::getMountManager()->addMount(new MountPoint( new NullStorage([]), '/' . $user . '/files' )); } \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user)); } /** * Get mounts from mount providers that are registered after setup * * @param MountProviderCollection $mountConfigManager * @param IUserManager $userManager */ private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) { if (!self::$listeningForProviders) { self::$listeningForProviders = true; $mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) { foreach (Filesystem::$usersSetup as $user => $setup) { $userObject = $userManager->get($user); if ($userObject) { $mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader()); array_walk($mounts, array(self::$mounts, 'addMount')); } } }); } } /** * get the default filesystem view * * @return View */ static public function getView() { return self::$defaultInstance; } /** * tear down the filesystem, removing all storage providers */ static public function tearDown() { self::clearMounts(); self::$defaultInstance = null; } /** * get the relative path of the root data directory for the current user * * @return string * * Returns path like /admin/files */ static public function getRoot() { if (!self::$defaultInstance) { return null; } return self::$defaultInstance->getRoot(); } /** * clear all mounts and storage backends */ public static function clearMounts() { if (self::$mounts) { self::$usersSetup = array(); self::$mounts->clear(); } } /** * mount an \OC\Files\Storage\Storage in our virtual filesystem * * @param \OC\Files\Storage\Storage|string $class * @param array $arguments * @param string $mountpoint */ static public function mount($class, $arguments, $mountpoint) { if (!self::$mounts) { \OC_Util::setupFS(); } $mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader()); self::$mounts->addMount($mount); } /** * return the path to a local version of the file * we need this because we can't know if a file is stored local or not from * outside the filestorage and for some purposes a local file is needed * * @param string $path * @return string */ static public function getLocalFile($path) { return self::$defaultInstance->getLocalFile($path); } /** * @param string $path * @return string */ static public function getLocalFolder($path) { return self::$defaultInstance->getLocalFolder($path); } /** * return path to file which reflects one visible in browser * * @param string $path * @return string */ static public function getLocalPath($path) { $datadir = \OC_User::getHome(\OC_User::getUser()) . '/files'; $newpath = $path; if (strncmp($newpath, $datadir, strlen($datadir)) == 0) { $newpath = substr($path, strlen($datadir)); } return $newpath; } /** * check if the requested path is valid * * @param string $path * @return bool */ static public function isValidPath($path) { $path = self::normalizePath($path); if (!$path || $path[0] !== '/') { $path = '/' . $path; } if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') { return false; } return true; } /** * checks if a file is blacklisted for storage in the filesystem * Listens to write and rename hooks * * @param array $data from hook */ static public function isBlacklisted($data) { if (isset($data['path'])) { $path = $data['path']; } else if (isset($data['newpath'])) { $path = $data['newpath']; } if (isset($path)) { if (self::isFileBlacklisted($path)) { $data['run'] = false; } } } /** * @param string $filename * @return bool */ static public function isFileBlacklisted($filename) { $filename = self::normalizePath($filename); $blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', array('.htaccess')); $filename = strtolower(basename($filename)); return in_array($filename, $blacklist); } /** * check if the directory should be ignored when scanning * NOTE: the special directories . and .. would cause never ending recursion * * @param String $dir * @return boolean */ static public function isIgnoredDir($dir) { if ($dir === '.' || $dir === '..') { return true; } return false; } /** * following functions are equivalent to their php builtin equivalents for arguments/return values. */ static public function mkdir($path) { return self::$defaultInstance->mkdir($path); } static public function rmdir($path) { return self::$defaultInstance->rmdir($path); } static public function opendir($path) { return self::$defaultInstance->opendir($path); } static public function readdir($path) { return self::$defaultInstance->readdir($path); } static public function is_dir($path) { return self::$defaultInstance->is_dir($path); } static public function is_file($path) { return self::$defaultInstance->is_file($path); } static public function stat($path) { return self::$defaultInstance->stat($path); } static public function filetype($path) { return self::$defaultInstance->filetype($path); } static public function filesize($path) { return self::$defaultInstance->filesize($path); } static public function readfile($path) { return self::$defaultInstance->readfile($path); } static public function isCreatable($path) { return self::$defaultInstance->isCreatable($path); } static public function isReadable($path) { return self::$defaultInstance->isReadable($path); } static public function isUpdatable($path) { return self::$defaultInstance->isUpdatable($path); } static public function isDeletable($path) { return self::$defaultInstance->isDeletable($path); } static public function isSharable($path) { return self::$defaultInstance->isSharable($path); } static public function file_exists($path) { return self::$defaultInstance->file_exists($path); } static public function filemtime($path) { return self::$defaultInstance->filemtime($path); } static public function touch($path, $mtime = null) { return self::$defaultInstance->touch($path, $mtime); } /** * @return string */ static public function file_get_contents($path) { return self::$defaultInstance->file_get_contents($path); } static public function file_put_contents($path, $data) { return self::$defaultInstance->file_put_contents($path, $data); } static public function unlink($path) { return self::$defaultInstance->unlink($path); } static public function rename($path1, $path2) { return self::$defaultInstance->rename($path1, $path2); } static public function copy($path1, $path2) { return self::$defaultInstance->copy($path1, $path2); } static public function fopen($path, $mode) { return self::$defaultInstance->fopen($path, $mode); } /** * @return string */ static public function toTmpFile($path) { return self::$defaultInstance->toTmpFile($path); } static public function fromTmpFile($tmpFile, $path) { return self::$defaultInstance->fromTmpFile($tmpFile, $path); } static public function getMimeType($path) { return self::$defaultInstance->getMimeType($path); } static public function hash($type, $path, $raw = false) { return self::$defaultInstance->hash($type, $path, $raw); } static public function free_space($path = '/') { return self::$defaultInstance->free_space($path); } static public function search($query) { return self::$defaultInstance->search($query); } /** * @param string $query */ static public function searchByMime($query) { return self::$defaultInstance->searchByMime($query); } /** * @param string|int $tag name or tag id * @param string $userId owner of the tags * @return FileInfo[] array or file info */ static public function searchByTag($tag, $userId) { return self::$defaultInstance->searchByTag($tag, $userId); } /** * check if a file or folder has been updated since $time * * @param string $path * @param int $time * @return bool */ static public function hasUpdated($path, $time) { return self::$defaultInstance->hasUpdated($path, $time); } /** * Fix common problems with a file path * * @param string $path * @param bool $stripTrailingSlash whether to strip the trailing slash * @param bool $isAbsolutePath whether the given path is absolute * @param bool $keepUnicode true to disable unicode normalization * @return string */ public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) { if (is_null(self::$normalizedPathCache)) { self::$normalizedPathCache = new CappedMemoryCache(); } /** * FIXME: This is a workaround for existing classes and files which call * this function with another type than a valid string. This * conversion should get removed as soon as all existing * function calls have been fixed. */ $path = (string)$path; $cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]); if (isset(self::$normalizedPathCache[$cacheKey])) { return self::$normalizedPathCache[$cacheKey]; } if ($path == '') { return '/'; } //normalize unicode if possible if (!$keepUnicode) { $path = \OC_Util::normalizeUnicode($path); } //no windows style slashes $path = str_replace('\\', '/', $path); //add leading slash if ($path[0] !== '/') { $path = '/' . $path; } // remove '/./' // ugly, but str_replace() can't replace them all in one go // as the replacement itself is part of the search string // which will only be found during the next iteration while (strpos($path, '/./') !== false) { $path = str_replace('/./', '/', $path); } // remove sequences of slashes $path = preg_replace('#/{2,}#', '/', $path); //remove trailing slash if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') { $path = substr($path, 0, -1); } // remove trailing '/.' if (substr($path, -2) == '/.') { $path = substr($path, 0, -2); } $normalizedPath = $path; self::$normalizedPathCache[$cacheKey] = $normalizedPath; return $normalizedPath; } /** * get the filesystem info * * @param string $path * @param boolean $includeMountPoints whether to add mountpoint sizes, * defaults to true * @return \OC\Files\FileInfo|bool False if file does not exist */ public static function getFileInfo($path, $includeMountPoints = true) { return self::$defaultInstance->getFileInfo($path, $includeMountPoints); } /** * change file metadata * * @param string $path * @param array $data * @return int * * returns the fileid of the updated file */ public static function putFileInfo($path, $data) { return self::$defaultInstance->putFileInfo($path, $data); } /** * get the content of a directory * * @param string $directory path under datadirectory * @param string $mimetype_filter limit returned content to this mimetype or mimepart * @return \OC\Files\FileInfo[] */ public static function getDirectoryContent($directory, $mimetype_filter = '') { return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); } /** * Get the path of a file by id * * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file * * @param int $id * @throws NotFoundException * @return string */ public static function getPath($id) { return self::$defaultInstance->getPath($id); } /** * Get the owner for a file or folder * * @param string $path * @return string */ public static function getOwner($path) { return self::$defaultInstance->getOwner($path); } /** * get the ETag for a file or folder * * @param string $path * @return string */ static public function getETag($path) { return self::$defaultInstance->getETag($path); } } private/Files/Type/TemplateManager.php 0000604 00000003061 15247130452 0013757 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Type; class TemplateManager { protected $templates = array(); public function registerTemplate($mimetype, $path) { $this->templates[$mimetype] = $path; } /** * get the path of the template for a mimetype * * @param string $mimetype * @return string|null */ public function getTemplatePath($mimetype) { if (isset($this->templates[$mimetype])) { return $this->templates[$mimetype]; } else { return null; } } /** * get the template content for a mimetype * * @param string $mimetype * @return string */ public function getTemplate($mimetype) { $path = $this->getTemplatePath($mimetype); if ($path) { return file_get_contents($path); } else { return ''; } } } private/Files/Type/Loader.php 0000604 00000010535 15247130452 0012123 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Type; use OCP\Files\IMimeTypeLoader; use OCP\IDBConnection; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; /** * Mimetype database loader * * @package OC\Files\Type */ class Loader implements IMimeTypeLoader { /** @var IDBConnection */ private $dbConnection; /** @var array [id => mimetype] */ protected $mimetypes; /** @var array [mimetype => id] */ protected $mimetypeIds; /** * @param IDBConnection $dbConnection */ public function __construct(IDBConnection $dbConnection) { $this->dbConnection = $dbConnection; $this->mimetypes = []; $this->mimetypeIds = []; } /** * Get a mimetype from its ID * * @param int $id * @return string|null */ public function getMimetypeById($id) { if (!$this->mimetypes) { $this->loadMimetypes(); } if (isset($this->mimetypes[$id])) { return $this->mimetypes[$id]; } return null; } /** * Get a mimetype ID, adding the mimetype to the DB if it does not exist * * @param string $mimetype * @return int */ public function getId($mimetype) { if (!$this->mimetypeIds) { $this->loadMimetypes(); } if (isset($this->mimetypeIds[$mimetype])) { return $this->mimetypeIds[$mimetype]; } return $this->store($mimetype); } /** * Test if a mimetype exists in the database * * @param string $mimetype * @return bool */ public function exists($mimetype) { if (!$this->mimetypeIds) { $this->loadMimetypes(); } return isset($this->mimetypeIds[$mimetype]); } /** * Clear all loaded mimetypes, allow for re-loading */ public function reset() { $this->mimetypes = []; $this->mimetypeIds = []; } /** * Store a mimetype in the DB * * @param string $mimetype * @param int inserted ID */ protected function store($mimetype) { try { $qb = $this->dbConnection->getQueryBuilder(); $qb->insert('mimetypes') ->values([ 'mimetype' => $qb->createNamedParameter($mimetype) ]); $qb->execute(); } catch (UniqueConstraintViolationException $e) { // something inserted it before us } $fetch = $this->dbConnection->getQueryBuilder(); $fetch->select('id') ->from('mimetypes') ->where( $fetch->expr()->eq('mimetype', $fetch->createNamedParameter($mimetype) )); $row = $fetch->execute()->fetch(); $this->mimetypes[$row['id']] = $mimetype; $this->mimetypeIds[$mimetype] = $row['id']; return $row['id']; } /** * Load all mimetypes from DB */ private function loadMimetypes() { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('id', 'mimetype') ->from('mimetypes'); $results = $qb->execute()->fetchAll(); foreach ($results as $row) { $this->mimetypes[$row['id']] = $row['mimetype']; $this->mimetypeIds[$row['mimetype']] = $row['id']; } } /** * Update filecache mimetype based on file extension * * @param string $ext file extension * @param int $mimeTypeId * @return int number of changed rows */ public function updateFilecache($ext, $mimeTypeId) { $folderMimeTypeId = $this->getId('httpd/unix-directory'); $update = $this->dbConnection->getQueryBuilder(); $update->update('filecache') ->set('mimetype', $update->createNamedParameter($mimeTypeId)) ->where($update->expr()->neq( 'mimetype', $update->createNamedParameter($mimeTypeId) )) ->andWhere($update->expr()->neq( 'mimetype', $update->createNamedParameter($folderMimeTypeId) )) ->andWhere($update->expr()->like( $update->createFunction('LOWER(' . $update->getColumnName('name') . ')'), $update->createNamedParameter('%' . $this->dbConnection->escapeLikeParameter('.' . $ext)) )); return $update->execute(); } } private/Files/Type/Detection.php 0000604 00000022660 15247130453 0012636 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Hendrik Leppelsack <hendrik@leppelsack.de> * @author Jens-Christian Fischer <jens-christian.fischer@switch.ch> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Type; use OCP\Files\IMimeTypeDetector; use OCP\IURLGenerator; /** * Class Detection * * Mimetype detection * * @package OC\Files\Type */ class Detection implements IMimeTypeDetector { protected $mimetypes = []; protected $secureMimeTypes = []; protected $mimetypeIcons = []; /** @var string[] */ protected $mimeTypeAlias = []; /** @var IURLGenerator */ private $urlGenerator; /** @var string */ private $customConfigDir; /** @var string */ private $defaultConfigDir; /** * @param IURLGenerator $urlGenerator * @param string $customConfigDir * @param string $defaultConfigDir */ public function __construct(IURLGenerator $urlGenerator, $customConfigDir, $defaultConfigDir) { $this->urlGenerator = $urlGenerator; $this->customConfigDir = $customConfigDir; $this->defaultConfigDir = $defaultConfigDir; } /** * Add an extension -> mimetype mapping * * $mimetype is the assumed correct mime type * The optional $secureMimeType is an alternative to send to send * to avoid potential XSS. * * @param string $extension * @param string $mimetype * @param string|null $secureMimeType */ public function registerType($extension, $mimetype, $secureMimeType = null) { $this->mimetypes[$extension] = array($mimetype, $secureMimeType); $this->secureMimeTypes[$mimetype] = $secureMimeType ?: $mimetype; } /** * Add an array of extension -> mimetype mappings * * The mimetype value is in itself an array where the first index is * the assumed correct mimetype and the second is either a secure alternative * or null if the correct is considered secure. * * @param array $types */ public function registerTypeArray($types) { $this->mimetypes = array_merge($this->mimetypes, $types); // Update the alternative mimetypes to avoid having to look them up each time. foreach ($this->mimetypes as $mimeType) { $this->secureMimeTypes[$mimeType[0]] = isset($mimeType[1]) ? $mimeType[1]: $mimeType[0]; } } /** * Add the mimetype aliases if they are not yet present */ private function loadAliases() { if (!empty($this->mimeTypeAlias)) { return; } $this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true); if (file_exists($this->customConfigDir . '/mimetypealiases.json')) { $custom = json_decode(file_get_contents($this->customConfigDir . '/mimetypealiases.json'), true); $this->mimeTypeAlias = array_merge($this->mimeTypeAlias, $custom); } } /** * @return string[] */ public function getAllAliases() { $this->loadAliases(); return $this->mimeTypeAlias; } /** * Add mimetype mappings if they are not yet present */ private function loadMappings() { if (!empty($this->mimetypes)) { return; } $mimetypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypemapping.dist.json'), true); //Check if need to load custom mappings if (file_exists($this->customConfigDir . '/mimetypemapping.json')) { $custom = json_decode(file_get_contents($this->customConfigDir . '/mimetypemapping.json'), true); $mimetypeMapping = array_merge($mimetypeMapping, $custom); } $this->registerTypeArray($mimetypeMapping); } /** * @return array */ public function getAllMappings() { $this->loadMappings(); return $this->mimetypes; } /** * detect mimetype only based on filename, content of file is not used * * @param string $path * @return string */ public function detectPath($path) { $this->loadMappings(); $fileName = basename($path); // remove leading dot on hidden files with a file extension $fileName = ltrim($fileName, '.'); // note: leading dot doesn't qualify as extension if (strpos($fileName, '.') > 0) { //try to guess the type by the file extension $extension = strtolower(strrchr($fileName, '.')); $extension = substr($extension, 1); //remove leading . return (isset($this->mimetypes[$extension]) && isset($this->mimetypes[$extension][0])) ? $this->mimetypes[$extension][0] : 'application/octet-stream'; } else { return 'application/octet-stream'; } } /** * detect mimetype based on both filename and content * * @param string $path * @return string */ public function detect($path) { $this->loadMappings(); if (@is_dir($path)) { // directories are easy return "httpd/unix-directory"; } $mimeType = $this->detectPath($path); if ($mimeType === 'application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo = finfo_open(FILEINFO_MIME) ) { $info = @strtolower(finfo_file($finfo, $path)); finfo_close($finfo); if ($info) { $mimeType = strpos($info, ';') !== false ? substr($info, 0, strpos($info, ';')) : $info; return empty($mimeType) ? 'application/octet-stream' : $mimeType; } } $isWrapped = (strpos($path, '://') !== false) and (substr($path, 0, 7) === 'file://'); if (!$isWrapped and $mimeType === 'application/octet-stream' && function_exists("mime_content_type")) { // use mime magic extension if available $mimeType = mime_content_type($path); } if (!$isWrapped and $mimeType === 'application/octet-stream' && \OC_Helper::canExecute("file")) { // it looks like we have a 'file' command, // lets see if it does have mime support $path = escapeshellarg($path); $fp = popen("file -b --mime-type $path 2>/dev/null", "r"); $reply = fgets($fp); pclose($fp); //trim the newline $mimeType = trim($reply); if (empty($mimeType)) { $mimeType = 'application/octet-stream'; } } return $mimeType; } /** * detect mimetype based on the content of a string * * @param string $data * @return string */ public function detectString($data) { if (function_exists('finfo_open') and function_exists('finfo_file')) { $finfo = finfo_open(FILEINFO_MIME); $info = finfo_buffer($finfo, $data); return strpos($info, ';') !== false ? substr($info, 0, strpos($info, ';')) : $info; } else { $tmpFile = \OC::$server->getTempManager()->getTemporaryFile(); $fh = fopen($tmpFile, 'wb'); fwrite($fh, $data, 8024); fclose($fh); $mime = $this->detect($tmpFile); unset($tmpFile); return $mime; } } /** * Get a secure mimetype that won't expose potential XSS. * * @param string $mimeType * @return string */ public function getSecureMimeType($mimeType) { $this->loadMappings(); return isset($this->secureMimeTypes[$mimeType]) ? $this->secureMimeTypes[$mimeType] : 'application/octet-stream'; } /** * Get path to the icon of a file type * @param string $mimetype the MIME type * @return string the url */ public function mimeTypeIcon($mimetype) { $this->loadAliases(); while (isset($this->mimeTypeAlias[$mimetype])) { $mimetype = $this->mimeTypeAlias[$mimetype]; } if (isset($this->mimetypeIcons[$mimetype])) { return $this->mimetypeIcons[$mimetype]; } // Replace slash and backslash with a minus $icon = str_replace('/', '-', $mimetype); $icon = str_replace('\\', '-', $icon); // Is it a dir? if ($mimetype === 'dir') { $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/folder.svg'); return $this->mimetypeIcons[$mimetype]; } if ($mimetype === 'dir-shared') { $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/folder-shared.svg'); return $this->mimetypeIcons[$mimetype]; } if ($mimetype === 'dir-external') { $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/folder-external.svg'); return $this->mimetypeIcons[$mimetype]; } // Icon exists? try { $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/' . $icon . '.svg'); return $this->mimetypeIcons[$mimetype]; } catch (\RuntimeException $e) { // Specified image not found } // Try only the first part of the filetype $mimePart = substr($icon, 0, strpos($icon, '-')); try { $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/' . $mimePart . '.svg'); return $this->mimetypeIcons[$mimetype]; } catch (\RuntimeException $e) { // Image for the first part of the mimetype not found } $this->mimetypeIcons[$mimetype] = $this->urlGenerator->imagePath('core', 'filetypes/file.svg'); return $this->mimetypeIcons[$mimetype]; } } private/Files/Mount/MoveableMount.php 0000604 00000002233 15247130453 0013650 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Mount; /** * Defines the mount point to be (re)moved by the user */ interface MoveableMount { /** * Move the mount point to $target * * @param string $target the target mount point * @return bool */ public function moveMount($target); /** * Remove the mount points * * @return mixed * @return bool */ public function removeMount(); } private/Files/Mount/Manager.php 0000604 00000007441 15247130453 0012453 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Mount; use \OC\Files\Filesystem; use OCP\Files\Mount\IMountManager; use OCP\Files\Mount\IMountPoint; class Manager implements IMountManager { /** * @var MountPoint[] */ private $mounts = array(); /** * @param IMountPoint $mount */ public function addMount(IMountPoint $mount) { $this->mounts[$mount->getMountPoint()] = $mount; } /** * @param string $mountPoint */ public function removeMount($mountPoint) { $mountPoint = Filesystem::normalizePath($mountPoint); if (strlen($mountPoint) > 1) { $mountPoint .= '/'; } unset($this->mounts[$mountPoint]); } /** * @param string $mountPoint * @param string $target */ public function moveMount($mountPoint, $target){ $this->mounts[$target] = $this->mounts[$mountPoint]; unset($this->mounts[$mountPoint]); } /** * Find the mount for $path * * @param string $path * @return MountPoint */ public function find($path) { \OC_Util::setupFS(); $path = $this->formatPath($path); if (isset($this->mounts[$path])) { return $this->mounts[$path]; } \OC_Hook::emit('OC_Filesystem', 'get_mountpoint', array('path' => $path)); $foundMountPoint = ''; $mountPoints = array_keys($this->mounts); foreach ($mountPoints as $mountpoint) { if (strpos($path, $mountpoint) === 0 and strlen($mountpoint) > strlen($foundMountPoint)) { $foundMountPoint = $mountpoint; } } if (isset($this->mounts[$foundMountPoint])) { return $this->mounts[$foundMountPoint]; } else { return null; } } /** * Find all mounts in $path * * @param string $path * @return MountPoint[] */ public function findIn($path) { \OC_Util::setupFS(); $path = $this->formatPath($path); $result = array(); $pathLength = strlen($path); $mountPoints = array_keys($this->mounts); foreach ($mountPoints as $mountPoint) { if (substr($mountPoint, 0, $pathLength) === $path and strlen($mountPoint) > $pathLength) { $result[] = $this->mounts[$mountPoint]; } } return $result; } public function clear() { $this->mounts = array(); } /** * Find mounts by storage id * * @param string $id * @return MountPoint[] */ public function findByStorageId($id) { \OC_Util::setupFS(); if (strlen($id) > 64) { $id = md5($id); } $result = array(); foreach ($this->mounts as $mount) { if ($mount->getStorageId() === $id) { $result[] = $mount; } } return $result; } /** * @return MountPoint[] */ public function getAll() { return $this->mounts; } /** * Find mounts by numeric storage id * * @param int $id * @return MountPoint[] */ public function findByNumericId($id) { $storageId = \OC\Files\Cache\Storage::getStorageId($id); return $this->findByStorageId($storageId); } /** * @param string $path * @return string */ private function formatPath($path) { $path = Filesystem::normalizePath($path); if (strlen($path) > 1) { $path .= '/'; } return $path; } } private/Files/Mount/ObjectHomeMountProvider.php 0000604 00000007377 15247130453 0015666 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Mount; use OCP\Files\Config\IHomeMountProvider; use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; use OCP\IUser; /** * Mount provider for object store home storages */ class ObjectHomeMountProvider implements IHomeMountProvider { /** * @var IConfig */ private $config; /** * ObjectStoreHomeMountProvider constructor. * * @param IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * Get the cache mount for a user * * @param IUser $user * @param IStorageFactory $loader * @return \OCP\Files\Mount\IMountPoint */ public function getHomeMountForUser(IUser $user, IStorageFactory $loader) { $config = $this->getMultiBucketObjectStoreConfig($user); if ($config === null) { $config = $this->getSingleBucketObjectStoreConfig($user); } if ($config === null) { return null; } return new MountPoint('\OC\Files\ObjectStore\HomeObjectStoreStorage', '/' . $user->getUID(), $config['arguments'], $loader); } /** * @param IUser $user * @return array|null */ private function getSingleBucketObjectStoreConfig(IUser $user) { $config = $this->config->getSystemValue('objectstore'); if (!is_array($config)) { return null; } // sanity checks if (empty($config['class'])) { \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR); } if (!isset($config['arguments'])) { $config['arguments'] = []; } $config['arguments']['user'] = $user; // instantiate object store implementation $config['arguments']['objectstore'] = new $config['class']($config['arguments']); return $config; } /** * @param IUser $user * @return array|null */ private function getMultiBucketObjectStoreConfig(IUser $user) { $config = $this->config->getSystemValue('objectstore_multibucket'); if (!is_array($config)) { return null; } // sanity checks if (empty($config['class'])) { \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR); } if (!isset($config['arguments'])) { $config['arguments'] = []; } $config['arguments']['user'] = $user; $bucket = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null); if ($bucket === null) { /* * Use any provided bucket argument as prefix * and add the mapping from username => bucket */ if (!isset($config['arguments']['bucket'])) { $config['arguments']['bucket'] = ''; } $mapper = new \OC\Files\ObjectStore\Mapper($user); $numBuckets = isset($config['arguments']['num_buckets']) ? $config['arguments']['num_buckets'] : 64; $config['arguments']['bucket'] .= $mapper->getBucket($numBuckets); $this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $config['arguments']['bucket']); } else { $config['arguments']['bucket'] = $bucket; } // instantiate object store implementation $config['arguments']['objectstore'] = new $config['class']($config['arguments']); return $config; } } private/Files/Mount/LocalHomeMountProvider.php 0000604 00000002513 15247130453 0015475 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Mount; use OCP\Files\Config\IHomeMountProvider; use OCP\Files\Storage\IStorageFactory; use OCP\IUser; /** * Mount provider for regular posix home folders */ class LocalHomeMountProvider implements IHomeMountProvider { /** * Get the cache mount for a user * * @param IUser $user * @param IStorageFactory $loader * @return \OCP\Files\Mount\IMountPoint[] */ public function getHomeMountForUser(IUser $user, IStorageFactory $loader) { $arguments = ['user' => $user]; return new MountPoint('\OC\Files\Storage\Home', '/' . $user->getUID(), $arguments, $loader); } } private/Files/Mount/MountPoint.php 0000604 00000015544 15247130453 0013220 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Georg Ehrke <georg@owncloud.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Mount; use \OC\Files\Filesystem; use OC\Files\Storage\StorageFactory; use OC\Files\Storage\Storage; use OCP\Files\Mount\IMountPoint; class MountPoint implements IMountPoint { /** * @var \OC\Files\Storage\Storage $storage */ protected $storage = null; protected $class; protected $storageId; protected $rootId = null; /** * Configuration options for the storage backend * * @var array */ protected $arguments = array(); protected $mountPoint; /** * Mount specific options * * @var array */ protected $mountOptions = array(); /** * @var \OC\Files\Storage\StorageFactory $loader */ private $loader; /** * Specified whether the storage is invalid after failing to * instantiate it. * * @var bool */ private $invalidStorage = false; /** @var int|null */ protected $mountId; /** * @param string|\OC\Files\Storage\Storage $storage * @param string $mountpoint * @param array $arguments (optional) configuration for the storage backend * @param \OCP\Files\Storage\IStorageFactory $loader * @param array $mountOptions mount specific options * @param int|null $mountId * @throws \Exception */ public function __construct($storage, $mountpoint, $arguments = null, $loader = null, $mountOptions = null, $mountId = null) { if (is_null($arguments)) { $arguments = array(); } if (is_null($loader)) { $this->loader = new StorageFactory(); } else { $this->loader = $loader; } if (!is_null($mountOptions)) { $this->mountOptions = $mountOptions; } $mountpoint = $this->formatPath($mountpoint); $this->mountPoint = $mountpoint; if ($storage instanceof Storage) { $this->class = get_class($storage); $this->storage = $this->loader->wrap($this, $storage); } else { // Update old classes to new namespace if (strpos($storage, 'OC_Filestorage_') !== false) { $storage = '\OC\Files\Storage\\' . substr($storage, 15); } $this->class = $storage; $this->arguments = $arguments; } $this->mountId = $mountId; } /** * get complete path to the mount point, relative to data/ * * @return string */ public function getMountPoint() { return $this->mountPoint; } /** * Sets the mount point path, relative to data/ * * @param string $mountPoint new mount point */ public function setMountPoint($mountPoint) { $this->mountPoint = $this->formatPath($mountPoint); } /** * create the storage that is mounted */ private function createStorage() { if ($this->invalidStorage) { return; } if (class_exists($this->class)) { try { $class = $this->class; // prevent recursion by setting the storage before applying wrappers $this->storage = new $class($this->arguments); $this->storage = $this->loader->wrap($this, $this->storage); } catch (\Exception $exception) { $this->storage = null; $this->invalidStorage = true; if ($this->mountPoint === '/') { // the root storage could not be initialized, show the user! throw new \Exception('The root storage could not be initialized. Please contact your local administrator.', $exception->getCode(), $exception); } else { \OCP\Util::writeLog('core', $exception->getMessage(), \OCP\Util::ERROR); } return; } } else { \OCP\Util::writeLog('core', 'storage backend ' . $this->class . ' not found', \OCP\Util::ERROR); $this->invalidStorage = true; return; } } /** * @return \OC\Files\Storage\Storage */ public function getStorage() { if (is_null($this->storage)) { $this->createStorage(); } return $this->storage; } /** * @return string */ public function getStorageId() { if (!$this->storageId) { if (is_null($this->storage)) { $storage = $this->createStorage(); //FIXME: start using exceptions if (is_null($storage)) { return null; } $this->storage = $storage; } $this->storageId = $this->storage->getId(); if (strlen($this->storageId) > 64) { $this->storageId = md5($this->storageId); } } return $this->storageId; } /** * @return int */ public function getNumericStorageId() { return $this->getStorage()->getStorageCache()->getNumericId(); } /** * @param string $path * @return string */ public function getInternalPath($path) { $path = Filesystem::normalizePath($path, true, false, true); if ($this->mountPoint === $path or $this->mountPoint . '/' === $path) { $internalPath = ''; } else { $internalPath = substr($path, strlen($this->mountPoint)); } // substr returns false instead of an empty string, we always want a string return (string)$internalPath; } /** * @param string $path * @return string */ private function formatPath($path) { $path = Filesystem::normalizePath($path); if (strlen($path) > 1) { $path .= '/'; } return $path; } /** * @param callable $wrapper */ public function wrapStorage($wrapper) { $storage = $this->getStorage(); // storage can be null if it couldn't be initialized if ($storage != null) { $this->storage = $wrapper($this->mountPoint, $storage, $this); } } /** * Get a mount option * * @param string $name Name of the mount option to get * @param mixed $default Default value for the mount option * @return mixed */ public function getOption($name, $default) { return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default; } /** * Get all options for the mount * * @return array */ public function getOptions() { return $this->mountOptions; } /** * Get the file id of the root of the storage * * @return int */ public function getStorageRootId() { if (is_null($this->rootId)) { $this->rootId = (int)$this->getStorage()->getCache()->getId(''); } return $this->rootId; } public function getMountId() { return $this->mountId; } public function getMountType() { return ''; } } private/Files/Mount/CacheMountProvider.php 0000604 00000003452 15247130453 0014640 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Mount; use OCP\Files\Config\IMountProvider; use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; use OCP\IUser; /** * Mount provider for custom cache storages */ class CacheMountProvider implements IMountProvider { /** * @var IConfig */ private $config; /** * ObjectStoreHomeMountProvider constructor. * * @param IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * Get the cache mount for a user * * @param IUser $user * @param IStorageFactory $loader * @return \OCP\Files\Mount\IMountPoint[] */ public function getMountsForUser(IUser $user, IStorageFactory $loader) { $cacheBaseDir = $this->config->getSystemValue('cache_path', ''); if ($cacheBaseDir !== '') { $cacheDir = rtrim($cacheBaseDir, '/') . '/' . $user->getUID(); if (!file_exists($cacheDir)) { mkdir($cacheDir, 0770, true); } return [ new MountPoint('\OC\Files\Storage\Local', '/' . $user->getUID() . '/cache', ['datadir' => $cacheDir, $loader]) ]; } else { return []; } } } private/Files/Stream/Encryption.php 0000604 00000034135 15247130453 0013364 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author jknockaert <jasper@knockaert.nl> * @author Lukas Reschke <lukas@statuscode.ch> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Stream; use Icewind\Streams\Wrapper; use OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException; class Encryption extends Wrapper { /** @var \OC\Encryption\Util */ protected $util; /** @var \OC\Encryption\File */ protected $file; /** @var \OCP\Encryption\IEncryptionModule */ protected $encryptionModule; /** @var \OC\Files\Storage\Storage */ protected $storage; /** @var \OC\Files\Storage\Wrapper\Encryption */ protected $encryptionStorage; /** @var string */ protected $internalPath; /** @var string */ protected $cache; /** @var integer */ protected $size; /** @var integer */ protected $position; /** @var integer */ protected $unencryptedSize; /** @var integer */ protected $headerSize; /** @var integer */ protected $unencryptedBlockSize; /** @var array */ protected $header; /** @var string */ protected $fullPath; /** @var bool */ protected $signed; /** * header data returned by the encryption module, will be written to the file * in case of a write operation * * @var array */ protected $newHeader; /** * user who perform the read/write operation null for public access * * @var string */ protected $uid; /** @var bool */ protected $readOnly; /** @var bool */ protected $writeFlag; /** @var array */ protected $expectedContextProperties; public function __construct() { $this->expectedContextProperties = array( 'source', 'storage', 'internalPath', 'fullPath', 'encryptionModule', 'header', 'uid', 'file', 'util', 'size', 'unencryptedSize', 'encryptionStorage', 'headerSize', 'signed' ); } /** * Wraps a stream with the provided callbacks * * @param resource $source * @param string $internalPath relative to mount point * @param string $fullPath relative to data/ * @param array $header * @param string $uid * @param \OCP\Encryption\IEncryptionModule $encryptionModule * @param \OC\Files\Storage\Storage $storage * @param \OC\Files\Storage\Wrapper\Encryption $encStorage * @param \OC\Encryption\Util $util * @param \OC\Encryption\File $file * @param string $mode * @param int $size * @param int $unencryptedSize * @param int $headerSize * @param bool $signed * @param string $wrapper stream wrapper class * @return resource * * @throws \BadMethodCallException */ public static function wrap($source, $internalPath, $fullPath, array $header, $uid, \OCP\Encryption\IEncryptionModule $encryptionModule, \OC\Files\Storage\Storage $storage, \OC\Files\Storage\Wrapper\Encryption $encStorage, \OC\Encryption\Util $util, \OC\Encryption\File $file, $mode, $size, $unencryptedSize, $headerSize, $signed, $wrapper = 'OC\Files\Stream\Encryption') { $context = stream_context_create(array( 'ocencryption' => array( 'source' => $source, 'storage' => $storage, 'internalPath' => $internalPath, 'fullPath' => $fullPath, 'encryptionModule' => $encryptionModule, 'header' => $header, 'uid' => $uid, 'util' => $util, 'file' => $file, 'size' => $size, 'unencryptedSize' => $unencryptedSize, 'encryptionStorage' => $encStorage, 'headerSize' => $headerSize, 'signed' => $signed ) )); return self::wrapSource($source, $context, 'ocencryption', $wrapper, $mode); } /** * add stream wrapper * * @param resource $source * @param string $mode * @param resource $context * @param string $protocol * @param string $class * @return resource * @throws \BadMethodCallException */ protected static function wrapSource($source, $context, $protocol, $class, $mode = 'r+') { try { stream_wrapper_register($protocol, $class); if (@rewinddir($source) === false) { $wrapped = fopen($protocol . '://', $mode, false, $context); } else { $wrapped = opendir($protocol . '://', $context); } } catch (\BadMethodCallException $e) { stream_wrapper_unregister($protocol); throw $e; } stream_wrapper_unregister($protocol); return $wrapped; } /** * Load the source from the stream context and return the context options * * @param string $name * @return array * @throws \BadMethodCallException */ protected function loadContext($name) { $context = parent::loadContext($name); foreach ($this->expectedContextProperties as $property) { if (array_key_exists($property, $context)) { $this->{$property} = $context[$property]; } else { throw new \BadMethodCallException('Invalid context, "' . $property . '" options not set'); } } return $context; } public function stream_open($path, $mode, $options, &$opened_path) { $this->loadContext('ocencryption'); $this->position = 0; $this->cache = ''; $this->writeFlag = false; $this->unencryptedBlockSize = $this->encryptionModule->getUnencryptedBlockSize($this->signed); if ( $mode === 'w' || $mode === 'w+' || $mode === 'wb' || $mode === 'wb+' || $mode === 'r+' || $mode === 'rb+' ) { $this->readOnly = false; } else { $this->readOnly = true; } $sharePath = $this->fullPath; if (!$this->storage->file_exists($this->internalPath)) { $sharePath = dirname($sharePath); } $accessList = $this->file->getAccessList($sharePath); $this->newHeader = $this->encryptionModule->begin($this->fullPath, $this->uid, $mode, $this->header, $accessList); if ( $mode === 'w' || $mode === 'w+' || $mode === 'wb' || $mode === 'wb+' ) { // We're writing a new file so start write counter with 0 bytes $this->unencryptedSize = 0; $this->writeHeader(); $this->headerSize = $this->util->getHeaderSize(); $this->size = $this->headerSize; } else { $this->skipHeader(); } return true; } public function stream_eof() { return $this->position >= $this->unencryptedSize; } public function stream_read($count) { $result = ''; $count = min($count, $this->unencryptedSize - $this->position); while ($count > 0) { $remainingLength = $count; // update the cache of the current block $this->readCache(); // determine the relative position in the current block $blockPosition = ($this->position % $this->unencryptedBlockSize); // if entire read inside current block then only position needs to be updated if ($remainingLength < ($this->unencryptedBlockSize - $blockPosition)) { $result .= substr($this->cache, $blockPosition, $remainingLength); $this->position += $remainingLength; $count = 0; // otherwise remainder of current block is fetched, the block is flushed and the position updated } else { $result .= substr($this->cache, $blockPosition); $this->flush(); $this->position += ($this->unencryptedBlockSize - $blockPosition); $count -= ($this->unencryptedBlockSize - $blockPosition); } } return $result; } public function stream_write($data) { $length = 0; // loop over $data to fit it in 6126 sized unencrypted blocks while (isset($data[0])) { $remainingLength = strlen($data); // set the cache to the current 6126 block $this->readCache(); // for seekable streams the pointer is moved back to the beginning of the encrypted block // flush will start writing there when the position moves to another block $positionInFile = (int)floor($this->position / $this->unencryptedBlockSize) * $this->util->getBlockSize() + $this->headerSize; $resultFseek = $this->parentStreamSeek($positionInFile); // only allow writes on seekable streams, or at the end of the encrypted stream if (!($this->readOnly) && ($resultFseek || $positionInFile === $this->size)) { // switch the writeFlag so flush() will write the block $this->writeFlag = true; // determine the relative position in the current block $blockPosition = ($this->position % $this->unencryptedBlockSize); // check if $data fits in current block // if so, overwrite existing data (if any) // update position and liberate $data if ($remainingLength < ($this->unencryptedBlockSize - $blockPosition)) { $this->cache = substr($this->cache, 0, $blockPosition) . $data . substr($this->cache, $blockPosition + $remainingLength); $this->position += $remainingLength; $length += $remainingLength; $data = ''; // if $data doesn't fit the current block, the fill the current block and reiterate // after the block is filled, it is flushed and $data is updatedxxx } else { $this->cache = substr($this->cache, 0, $blockPosition) . substr($data, 0, $this->unencryptedBlockSize - $blockPosition); $this->flush(); $this->position += ($this->unencryptedBlockSize - $blockPosition); $length += ($this->unencryptedBlockSize - $blockPosition); $data = substr($data, $this->unencryptedBlockSize - $blockPosition); } } else { $data = ''; } $this->unencryptedSize = max($this->unencryptedSize, $this->position); } return $length; } public function stream_tell() { return $this->position; } public function stream_seek($offset, $whence = SEEK_SET) { $return = false; switch ($whence) { case SEEK_SET: $newPosition = $offset; break; case SEEK_CUR: $newPosition = $this->position + $offset; break; case SEEK_END: $newPosition = $this->unencryptedSize + $offset; break; default: return $return; } if ($newPosition > $this->unencryptedSize || $newPosition < 0) { return $return; } $newFilePosition = floor($newPosition / $this->unencryptedBlockSize) * $this->util->getBlockSize() + $this->headerSize; $oldFilePosition = parent::stream_tell(); if ($this->parentStreamSeek($newFilePosition)) { $this->parentStreamSeek($oldFilePosition); $this->flush(); $this->parentStreamSeek($newFilePosition); $this->position = $newPosition; $return = true; } return $return; } public function stream_close() { $this->flush('end'); $position = (int)floor($this->position/$this->unencryptedBlockSize); $remainingData = $this->encryptionModule->end($this->fullPath, $position . 'end'); if ($this->readOnly === false) { if(!empty($remainingData)) { parent::stream_write($remainingData); } $this->encryptionStorage->updateUnencryptedSize($this->fullPath, $this->unencryptedSize); } return parent::stream_close(); } /** * write block to file * @param string $positionPrefix */ protected function flush($positionPrefix = '') { // write to disk only when writeFlag was set to 1 if ($this->writeFlag) { // Disable the file proxies so that encryption is not // automatically attempted when the file is written to disk - // we are handling that separately here and we don't want to // get into an infinite loop $position = (int)floor($this->position/$this->unencryptedBlockSize); $encrypted = $this->encryptionModule->encrypt($this->cache, $position . $positionPrefix); $bytesWritten = parent::stream_write($encrypted); $this->writeFlag = false; // Check whether the write concerns the last block // If so then update the encrypted filesize // Note that the unencrypted pointer and filesize are NOT yet updated when flush() is called // We recalculate the encrypted filesize as we do not know the context of calling flush() $completeBlocksInFile=(int)floor($this->unencryptedSize/$this->unencryptedBlockSize); if ($completeBlocksInFile === (int)floor($this->position/$this->unencryptedBlockSize)) { $this->size = $this->util->getBlockSize() * $completeBlocksInFile; $this->size += $bytesWritten; $this->size += $this->headerSize; } } // always empty the cache (otherwise readCache() will not fill it with the new block) $this->cache = ''; } /** * read block to file */ protected function readCache() { // cache should always be empty string when this function is called // don't try to fill the cache when trying to write at the end of the unencrypted file when it coincides with new block if ($this->cache === '' && !($this->position === $this->unencryptedSize && ($this->position % $this->unencryptedBlockSize) === 0)) { // Get the data from the file handle $data = parent::stream_read($this->util->getBlockSize()); $position = (int)floor($this->position/$this->unencryptedBlockSize); $numberOfChunks = (int)($this->unencryptedSize / $this->unencryptedBlockSize); if($numberOfChunks === $position) { $position .= 'end'; } $this->cache = $this->encryptionModule->decrypt($data, $position); } } /** * write header at beginning of encrypted file * * @return integer * @throws EncryptionHeaderKeyExistsException if header key is already in use */ protected function writeHeader() { $header = $this->util->createHeader($this->newHeader, $this->encryptionModule); return parent::stream_write($header); } /** * read first block to skip the header */ protected function skipHeader() { parent::stream_read($this->headerSize); } /** * call stream_seek() from parent class * * @param integer $position * @return bool */ protected function parentStreamSeek($position) { return parent::stream_seek($position); } /** * @param string $path * @param array $options * @return bool */ public function dir_opendir($path, $options) { return false; } } private/Files/Stream/Quota.php 0000604 00000005341 15247130453 0012320 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Stream; use Icewind\Streams\Wrapper; /** * stream wrapper limits the amount of data that can be written to a stream * * usage: resource \OC\Files\Stream\Quota::wrap($stream, $limit) */ class Quota extends Wrapper { /** * @var int $limit */ private $limit; /** * @param resource $stream * @param int $limit * @return resource */ static public function wrap($stream, $limit) { $context = stream_context_create(array( 'quota' => array( 'source' => $stream, 'limit' => $limit ) )); return Wrapper::wrapSource($stream, $context, 'quota', self::class); } public function stream_open($path, $mode, $options, &$opened_path) { $context = $this->loadContext('quota'); $this->source = $context['source']; $this->limit = $context['limit']; return true; } public function dir_opendir($path, $options) { return false; } public function stream_seek($offset, $whence = SEEK_SET) { if ($whence === SEEK_END){ // go to the end to find out last position's offset $oldOffset = $this->stream_tell(); if (fseek($this->source, 0, $whence) !== 0){ return false; } $whence = SEEK_SET; $offset = $this->stream_tell() + $offset; $this->limit += $oldOffset - $offset; } else if ($whence === SEEK_SET) { $this->limit += $this->stream_tell() - $offset; } else { $this->limit -= $offset; } // this wrapper needs to return "true" for success. // the fseek call itself returns 0 on succeess return fseek($this->source, $offset, $whence) === 0; } public function stream_read($count) { $this->limit -= $count; return fread($this->source, $count); } public function stream_write($data) { $size = strlen($data); if ($size > $this->limit) { $data = substr($data, 0, $this->limit); $size = $this->limit; } $this->limit -= $size; return fwrite($this->source, $data); } } private/Files/Node/NonExistingFolder.php 0000604 00000006662 15247130453 0014271 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Node; use OCP\Files\NotFoundException; class NonExistingFolder extends Folder { /** * @param string $newPath * @throws \OCP\Files\NotFoundException */ public function rename($newPath) { throw new NotFoundException(); } public function delete() { throw new NotFoundException(); } public function copy($newPath) { throw new NotFoundException(); } public function touch($mtime = null) { throw new NotFoundException(); } public function getId() { if ($this->fileInfo) { return parent::getId(); } else { throw new NotFoundException(); } } public function stat() { throw new NotFoundException(); } public function getMTime() { if ($this->fileInfo) { return parent::getMTime(); } else { throw new NotFoundException(); } } public function getSize() { if ($this->fileInfo) { return parent::getSize(); } else { throw new NotFoundException(); } } public function getEtag() { if ($this->fileInfo) { return parent::getEtag(); } else { throw new NotFoundException(); } } public function getPermissions() { if ($this->fileInfo) { return parent::getPermissions(); } else { throw new NotFoundException(); } } public function isReadable() { if ($this->fileInfo) { return parent::isReadable(); } else { throw new NotFoundException(); } } public function isUpdateable() { if ($this->fileInfo) { return parent::isUpdateable(); } else { throw new NotFoundException(); } } public function isDeletable() { if ($this->fileInfo) { return parent::isDeletable(); } else { throw new NotFoundException(); } } public function isShareable() { if ($this->fileInfo) { return parent::isShareable(); } else { throw new NotFoundException(); } } public function get($path) { throw new NotFoundException(); } public function getDirectoryListing() { throw new NotFoundException(); } public function nodeExists($path) { return false; } public function newFolder($path) { throw new NotFoundException(); } public function newFile($path) { throw new NotFoundException(); } public function search($pattern) { throw new NotFoundException(); } public function searchByMime($mime) { throw new NotFoundException(); } public function searchByTag($tag, $userId) { throw new NotFoundException(); } public function getById($id) { throw new NotFoundException(); } public function getFreeSpace() { throw new NotFoundException(); } public function isCreatable() { if ($this->fileInfo) { return parent::isCreatable(); } else { throw new NotFoundException(); } } } private/Files/Node/Folder.php 0000604 00000033520 15247130453 0012074 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Node; use OC\DB\QueryBuilder\Literal; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\FileInfo; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\Search\ISearchOperator; class Folder extends Node implements \OCP\Files\Folder { /** * Creates a Folder that represents a non-existing path * * @param string $path path * @return string non-existing node class */ protected function createNonExistingNode($path) { return new NonExistingFolder($this->root, $this->view, $path); } /** * @param string $path path relative to the folder * @return string * @throws \OCP\Files\NotPermittedException */ public function getFullPath($path) { if (!$this->isValidPath($path)) { throw new NotPermittedException('Invalid path'); } return $this->path . $this->normalizePath($path); } /** * @param string $path * @return string */ public function getRelativePath($path) { if ($this->path === '' or $this->path === '/') { return $this->normalizePath($path); } if ($path === $this->path) { return '/'; } else if (strpos($path, $this->path . '/') !== 0) { return null; } else { $path = substr($path, strlen($this->path)); return $this->normalizePath($path); } } /** * check if a node is a (grand-)child of the folder * * @param \OC\Files\Node\Node $node * @return bool */ public function isSubNode($node) { return strpos($node->getPath(), $this->path . '/') === 0; } /** * get the content of this directory * * @throws \OCP\Files\NotFoundException * @return Node[] */ public function getDirectoryListing() { $folderContent = $this->view->getDirectoryContent($this->path); return array_map(function (FileInfo $info) { if ($info->getMimetype() === 'httpd/unix-directory') { return new Folder($this->root, $this->view, $info->getPath(), $info); } else { return new File($this->root, $this->view, $info->getPath(), $info); } }, $folderContent); } /** * @param string $path * @param FileInfo $info * @return File|Folder */ protected function createNode($path, FileInfo $info = null) { if (is_null($info)) { $isDir = $this->view->is_dir($path); } else { $isDir = $info->getType() === FileInfo::TYPE_FOLDER; } if ($isDir) { return new Folder($this->root, $this->view, $path, $info); } else { return new File($this->root, $this->view, $path, $info); } } /** * Get the node at $path * * @param string $path * @return \OC\Files\Node\Node * @throws \OCP\Files\NotFoundException */ public function get($path) { return $this->root->get($this->getFullPath($path)); } /** * @param string $path * @return bool */ public function nodeExists($path) { try { $this->get($path); return true; } catch (NotFoundException $e) { return false; } } /** * @param string $path * @return \OC\Files\Node\Folder * @throws \OCP\Files\NotPermittedException */ public function newFolder($path) { if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath); $this->root->emit('\OC\Files', 'preWrite', array($nonExisting)); $this->root->emit('\OC\Files', 'preCreate', array($nonExisting)); $this->view->mkdir($fullPath); $node = new Folder($this->root, $this->view, $fullPath); $this->root->emit('\OC\Files', 'postWrite', array($node)); $this->root->emit('\OC\Files', 'postCreate', array($node)); return $node; } else { throw new NotPermittedException('No create permission for folder'); } } /** * @param string $path * @return \OC\Files\Node\File * @throws \OCP\Files\NotPermittedException */ public function newFile($path) { if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath); $this->root->emit('\OC\Files', 'preWrite', array($nonExisting)); $this->root->emit('\OC\Files', 'preCreate', array($nonExisting)); $this->view->touch($fullPath); $node = new File($this->root, $this->view, $fullPath); $this->root->emit('\OC\Files', 'postWrite', array($node)); $this->root->emit('\OC\Files', 'postCreate', array($node)); return $node; } else { throw new NotPermittedException('No create permission for path'); } } /** * search for files with the name matching $query * * @param string|ISearchOperator $query * @return \OC\Files\Node\Node[] */ public function search($query) { if (is_string($query)) { return $this->searchCommon('search', array('%' . $query . '%')); } else { return $this->searchCommon('searchQuery', array($query)); } } /** * search for files by mimetype * * @param string $mimetype * @return Node[] */ public function searchByMime($mimetype) { return $this->searchCommon('searchByMime', array($mimetype)); } /** * search for files by tag * * @param string|int $tag name or tag id * @param string $userId owner of the tags * @return Node[] */ public function searchByTag($tag, $userId) { return $this->searchCommon('searchByTag', array($tag, $userId)); } /** * @param string $method cache method * @param array $args call args * @return \OC\Files\Node\Node[] */ private function searchCommon($method, $args) { $files = array(); $rootLength = strlen($this->path); $mount = $this->root->getMount($this->path); $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($this->path); $internalPath = rtrim($internalPath, '/'); if ($internalPath !== '') { $internalPath = $internalPath . '/'; } $internalRootLength = strlen($internalPath); $cache = $storage->getCache(''); $results = call_user_func_array(array($cache, $method), $args); foreach ($results as $result) { if ($internalRootLength === 0 or substr($result['path'], 0, $internalRootLength) === $internalPath) { $result['internalPath'] = $result['path']; $result['path'] = substr($result['path'], $internalRootLength); $result['storage'] = $storage; $files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount); } } $mounts = $this->root->getMountsIn($this->path); foreach ($mounts as $mount) { $storage = $mount->getStorage(); if ($storage) { $cache = $storage->getCache(''); $relativeMountPoint = substr($mount->getMountPoint(), $rootLength); $results = call_user_func_array(array($cache, $method), $args); foreach ($results as $result) { $result['internalPath'] = $result['path']; $result['path'] = $relativeMountPoint . $result['path']; $result['storage'] = $storage; $files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount); } } } return array_map(function (FileInfo $file) { return $this->createNode($file->getPath(), $file); }, $files); } /** * @param int $id * @return \OC\Files\Node\Node[] */ public function getById($id) { $mountCache = $this->root->getUserMountCache(); if (strpos($this->getPath(), '/', 1) > 0) { list(, $user) = explode('/', $this->getPath()); } else { $user = null; } $mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user); $mounts = $this->root->getMountsIn($this->path); $mounts[] = $this->root->getMount($this->path); /** @var IMountPoint[] $folderMounts */ $folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) { return $mountPoint->getMountPoint(); }, $mounts), $mounts); /** @var ICachedMountInfo[] $mountsContainingFile */ $mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) { return isset($folderMounts[$cachedMountInfo->getMountPoint()]); })); if (count($mountsContainingFile) === 0) { return []; } // we only need to get the cache info once, since all mounts we found point to the same storage $mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()]; $cacheEntry = $mount->getStorage()->getCache()->get((int)$id); if (!$cacheEntry) { return []; } // cache jails will hide the "true" internal path $internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/'); $nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) { $mount = $folderMounts[$cachedMountInfo->getMountPoint()]; $pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath())); $pathRelativeToMount = ltrim($pathRelativeToMount, '/'); $absolutePath = $cachedMountInfo->getMountPoint() . $pathRelativeToMount; return $this->root->createNode($absolutePath, new \OC\Files\FileInfo( $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount, \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount)) )); }, $mountsContainingFile); return array_filter($nodes, function (Node $node) { return $this->getRelativePath($node->getPath()); }); } public function getFreeSpace() { return $this->view->free_space($this->path); } public function delete() { if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) { $this->sendHooks(array('preDelete')); $fileInfo = $this->getFileInfo(); $this->view->rmdir($this->path); $nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo); $this->root->emit('\OC\Files', 'postDelete', array($nonExisting)); $this->exists = false; } else { throw new NotPermittedException('No delete permission for path'); } } /** * Add a suffix to the name in case the file exists * * @param string $name * @return string * @throws NotPermittedException */ public function getNonExistingName($name) { $uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view); return trim($this->getRelativePath($uniqueName), '/'); } /** * @param int $limit * @param int $offset * @return \OCP\Files\Node[] */ public function getRecent($limit, $offset = 0) { $mimetypeLoader = \OC::$server->getMimeTypeLoader(); $mounts = $this->root->getMountsIn($this->path); $mounts[] = $this->getMountPoint(); $mounts = array_filter($mounts, function (IMountPoint $mount) { return $mount->getStorage(); }); $storageIds = array_map(function (IMountPoint $mount) { return $mount->getStorage()->getCache()->getNumericStorageId(); }, $mounts); /** @var IMountPoint[] $mountMap */ $mountMap = array_combine($storageIds, $mounts); $folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER); //todo look into options of filtering path based on storage id (only search in files/ for home storage, filter by share root for shared, etc) $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); $query = $builder ->select('f.*') ->from('filecache', 'f') ->andWhere($builder->expr()->in('f.storage', $builder->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY))) ->andWhere($builder->expr()->orX( // handle non empty folders separate $builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)), $builder->expr()->eq('f.size', new Literal(0)) )) ->orderBy('f.mtime', 'DESC') ->setMaxResults($limit) ->setFirstResult($offset); $result = $query->execute()->fetchAll(); $files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) { $mount = $mountMap[$entry['storage']]; $entry['internalPath'] = $entry['path']; $entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']); $entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']); $path = $this->getAbsolutePath($mount, $entry['path']); if (is_null($path)) { return null; } $fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount); return $this->root->createNode($fileInfo->getPath(), $fileInfo); }, $result)); return array_values(array_filter($files, function (Node $node) { $relative = $this->getRelativePath($node->getPath()); return $relative !== null && $relative !== '/'; })); } private function getAbsolutePath(IMountPoint $mount, $path) { $storage = $mount->getStorage(); if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) { /** @var \OC\Files\Storage\Wrapper\Jail $storage */ $jailRoot = $storage->getUnjailedPath(''); $rootLength = strlen($jailRoot) + 1; if ($path === $jailRoot) { return $mount->getMountPoint(); } else if (substr($path, 0, $rootLength) === $jailRoot . '/') { return $mount->getMountPoint() . substr($path, $rootLength); } else { return null; } } else { return $mount->getMountPoint() . $path; } } } private/Files/Node/NonExistingFile.php 0000604 00000005603 15247130453 0013727 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Node; use OCP\Files\NotFoundException; class NonExistingFile extends File { /** * @param string $newPath * @throws \OCP\Files\NotFoundException */ public function rename($newPath) { throw new NotFoundException(); } public function delete() { throw new NotFoundException(); } public function copy($newPath) { throw new NotFoundException(); } public function touch($mtime = null) { throw new NotFoundException(); } public function getId() { if ($this->fileInfo) { return parent::getId(); } else { throw new NotFoundException(); } } public function stat() { throw new NotFoundException(); } public function getMTime() { if ($this->fileInfo) { return parent::getMTime(); } else { throw new NotFoundException(); } } public function getSize() { if ($this->fileInfo) { return parent::getSize(); } else { throw new NotFoundException(); } } public function getEtag() { if ($this->fileInfo) { return parent::getEtag(); } else { throw new NotFoundException(); } } public function getPermissions() { if ($this->fileInfo) { return parent::getPermissions(); } else { throw new NotFoundException(); } } public function isReadable() { if ($this->fileInfo) { return parent::isReadable(); } else { throw new NotFoundException(); } } public function isUpdateable() { if ($this->fileInfo) { return parent::isUpdateable(); } else { throw new NotFoundException(); } } public function isDeletable() { if ($this->fileInfo) { return parent::isDeletable(); } else { throw new NotFoundException(); } } public function isShareable() { if ($this->fileInfo) { return parent::isShareable(); } else { throw new NotFoundException(); } } public function getContent() { throw new NotFoundException(); } public function putContent($data) { throw new NotFoundException(); } public function getMimeType() { if ($this->fileInfo) { return parent::getMimeType(); } else { throw new NotFoundException(); } } public function fopen($mode) { throw new NotFoundException(); } } private/Files/Node/HookConnector.php 0000604 00000011676 15247130453 0013444 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Node; use OCP\Files\FileInfo; use OC\Files\Filesystem; use OC\Files\View; use OCP\Util; class HookConnector { /** * @var Root */ private $root; /** * @var View */ private $view; /** * @var FileInfo[] */ private $deleteMetaCache = []; /** * HookConnector constructor. * * @param Root $root * @param View $view */ public function __construct(Root $root, View $view) { $this->root = $root; $this->view = $view; } public function viewToNode() { Util::connectHook('OC_Filesystem', 'write', $this, 'write'); Util::connectHook('OC_Filesystem', 'post_write', $this, 'postWrite'); Util::connectHook('OC_Filesystem', 'create', $this, 'create'); Util::connectHook('OC_Filesystem', 'post_create', $this, 'postCreate'); Util::connectHook('OC_Filesystem', 'delete', $this, 'delete'); Util::connectHook('OC_Filesystem', 'post_delete', $this, 'postDelete'); Util::connectHook('OC_Filesystem', 'rename', $this, 'rename'); Util::connectHook('OC_Filesystem', 'post_rename', $this, 'postRename'); Util::connectHook('OC_Filesystem', 'copy', $this, 'copy'); Util::connectHook('OC_Filesystem', 'post_copy', $this, 'postCopy'); Util::connectHook('OC_Filesystem', 'touch', $this, 'touch'); Util::connectHook('OC_Filesystem', 'post_touch', $this, 'postTouch'); } public function write($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preWrite', [$node]); } public function postWrite($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postWrite', [$node]); } public function create($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preCreate', [$node]); } public function postCreate($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postCreate', [$node]); } public function delete($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->deleteMetaCache[$node->getPath()] = $node->getFileInfo(); $this->root->emit('\OC\Files', 'preDelete', [$node]); } public function postDelete($arguments) { $node = $this->getNodeForPath($arguments['path']); unset($this->deleteMetaCache[$node->getPath()]); $this->root->emit('\OC\Files', 'postDelete', [$node]); } public function touch($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preTouch', [$node]); } public function postTouch($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postTouch', [$node]); } public function rename($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'preRename', [$source, $target]); } public function postRename($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'postRename', [$source, $target]); } public function copy($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'preCopy', [$source, $target]); } public function postCopy($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'postCopy', [$source, $target]); } private function getNodeForPath($path) { $info = Filesystem::getView()->getFileInfo($path); if (!$info) { $fullPath = Filesystem::getView()->getAbsolutePath($path); if (isset($this->deleteMetaCache[$fullPath])) { $info = $this->deleteMetaCache[$fullPath]; } else { $info = null; } if (Filesystem::is_dir($path)) { return new NonExistingFolder($this->root, $this->view, $fullPath, $info); } else { return new NonExistingFile($this->root, $this->view, $fullPath, $info); } } if ($info->getType() === FileInfo::TYPE_FILE) { return new File($this->root, $this->view, $info->getPath(), $info); } else { return new Folder($this->root, $this->view, $info->getPath(), $info); } } } private/Files/Node/File.php 0000604 00000007144 15247130453 0011543 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Node; use OCP\Files\NotPermittedException; class File extends Node implements \OCP\Files\File { /** * Creates a Folder that represents a non-existing path * * @param string $path path * @return string non-existing node class */ protected function createNonExistingNode($path) { return new NonExistingFile($this->root, $this->view, $path); } /** * @return string * @throws \OCP\Files\NotPermittedException */ public function getContent() { if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) { /** * @var \OC\Files\Storage\Storage $storage; */ return $this->view->file_get_contents($this->path); } else { throw new NotPermittedException(); } } /** * @param string $data * @throws \OCP\Files\NotPermittedException */ public function putContent($data) { if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) { $this->sendHooks(array('preWrite')); $this->view->file_put_contents($this->path, $data); $this->fileInfo = null; $this->sendHooks(array('postWrite')); } else { throw new NotPermittedException(); } } /** * @param string $mode * @return resource * @throws \OCP\Files\NotPermittedException */ public function fopen($mode) { $preHooks = array(); $postHooks = array(); $requiredPermissions = \OCP\Constants::PERMISSION_READ; switch ($mode) { case 'r+': case 'rb+': case 'w+': case 'wb+': case 'x+': case 'xb+': case 'a+': case 'ab+': case 'w': case 'wb': case 'x': case 'xb': case 'a': case 'ab': $preHooks[] = 'preWrite'; $postHooks[] = 'postWrite'; $requiredPermissions |= \OCP\Constants::PERMISSION_UPDATE; break; } if ($this->checkPermissions($requiredPermissions)) { $this->sendHooks($preHooks); $result = $this->view->fopen($this->path, $mode); $this->sendHooks($postHooks); return $result; } else { throw new NotPermittedException(); } } public function delete() { if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) { $this->sendHooks(array('preDelete')); $fileInfo = $this->getFileInfo(); $this->view->unlink($this->path); $nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo); $this->root->emit('\OC\Files', 'postDelete', array($nonExisting)); $this->exists = false; $this->fileInfo = null; } else { throw new NotPermittedException(); } } /** * @param string $type * @param bool $raw * @return string */ public function hash($type, $raw = false) { return $this->view->hash($type, $this->path, $raw); } /** * @inheritdoc */ public function getChecksum() { return $this->getFileInfo()->getChecksum(); } } private/Files/Node/LazyRoot.php 0000604 00000021302 15247130453 0012437 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Node; use OCP\Files\IRootFolder; /** * Class LazyRoot * * This is a lazy wrapper around the root. So only * once it is needed this will get initialized. * * @package OC\Files\Node */ class LazyRoot implements IRootFolder { /** @var \Closure */ private $rootFolderClosure; /** @var IRootFolder */ private $rootFolder; /** * LazyRoot constructor. * * @param \Closure $rootFolderClosure */ public function __construct(\Closure $rootFolderClosure) { $this->rootFolderClosure = $rootFolderClosure; } /** * Magic method to first get the real rootFolder and then * call $method with $args on it * * @param $method * @param $args * @return mixed */ public function __call($method, $args) { if ($this->rootFolder === null) { $this->rootFolder = call_user_func($this->rootFolderClosure); } return call_user_func_array([$this->rootFolder, $method], $args); } /** * @inheritDoc */ public function getUser() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function listen($scope, $method, callable $callback) { $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function removeListener($scope = null, $method = null, callable $callback = null) { $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function emit($scope, $method, $arguments = array()) { $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function mount($storage, $mountPoint, $arguments = array()) { $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getMount($mountPoint) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getMountsIn($mountPoint) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getMountByStorageId($storageId) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getMountByNumericStorageId($numericId) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function unMount($mount) { $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function get($path) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function rename($targetPath) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function delete() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function copy($targetPath) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function touch($mtime = null) { $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getStorage() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getPath() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getInternalPath() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getId() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function stat() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getMTime() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getSize() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getEtag() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getPermissions() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isReadable() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isUpdateable() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isDeletable() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isShareable() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getParent() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getName() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getUserFolder($userId) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getMimetype() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getMimePart() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isEncrypted() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getType() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isShared() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isMounted() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getMountPoint() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getOwner() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getChecksum() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getFullPath($path) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getRelativePath($path) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isSubNode($node) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getDirectoryListing() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function nodeExists($path) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function newFolder($path) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function newFile($path) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function search($query) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function searchByMime($mimetype) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function searchByTag($tag, $userId) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getById($id) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getFreeSpace() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function isCreatable() { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getNonExistingName($name) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function move($targetPath) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function lock($type) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function changeLock($targetType) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function unlock($type) { return $this->__call(__FUNCTION__, func_get_args()); } /** * @inheritDoc */ public function getRecent($limit, $offset = 0) { return $this->__call(__FUNCTION__, func_get_args()); } } private/Files/Node/Root.php 0000604 00000021143 15247130453 0011602 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Node; use OC\Cache\CappedMemoryCache; use OC\Files\Mount\Manager; use OC\Files\Mount\MountPoint; use OCP\Files\Config\IUserMountCache; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OC\Hooks\PublicEmitter; use OCP\Files\IRootFolder; use OCP\ILogger; use OCP\IUserManager; /** * Class Root * * Hooks available in scope \OC\Files * - preWrite(\OCP\Files\Node $node) * - postWrite(\OCP\Files\Node $node) * - preCreate(\OCP\Files\Node $node) * - postCreate(\OCP\Files\Node $node) * - preDelete(\OCP\Files\Node $node) * - postDelete(\OCP\Files\Node $node) * - preTouch(\OC\FilesP\Node $node, int $mtime) * - postTouch(\OCP\Files\Node $node) * - preCopy(\OCP\Files\Node $source, \OCP\Files\Node $target) * - postCopy(\OCP\Files\Node $source, \OCP\Files\Node $target) * - preRename(\OCP\Files\Node $source, \OCP\Files\Node $target) * - postRename(\OCP\Files\Node $source, \OCP\Files\Node $target) * * @package OC\Files\Node */ class Root extends Folder implements IRootFolder { /** @var Manager */ private $mountManager; /** @var PublicEmitter */ private $emitter; /** @var null|\OC\User\User */ private $user; /** @var CappedMemoryCache */ private $userFolderCache; /** @var IUserMountCache */ private $userMountCache; /** @var ILogger */ private $logger; /** @var IUserManager */ private $userManager; /** * @param \OC\Files\Mount\Manager $manager * @param \OC\Files\View $view * @param \OC\User\User|null $user * @param IUserMountCache $userMountCache * @param ILogger $logger * @param IUserManager $userManager */ public function __construct($manager, $view, $user, IUserMountCache $userMountCache, ILogger $logger, IUserManager $userManager) { parent::__construct($this, $view, ''); $this->mountManager = $manager; $this->user = $user; $this->emitter = new PublicEmitter(); $this->userFolderCache = new CappedMemoryCache(); $this->userMountCache = $userMountCache; $this->logger = $logger; $this->userManager = $userManager; } /** * Get the user for which the filesystem is setup * * @return \OC\User\User */ public function getUser() { return $this->user; } /** * @param string $scope * @param string $method * @param callable $callback */ public function listen($scope, $method, callable $callback) { $this->emitter->listen($scope, $method, $callback); } /** * @param string $scope optional * @param string $method optional * @param callable $callback optional */ public function removeListener($scope = null, $method = null, callable $callback = null) { $this->emitter->removeListener($scope, $method, $callback); } /** * @param string $scope * @param string $method * @param Node[] $arguments */ public function emit($scope, $method, $arguments = array()) { $this->emitter->emit($scope, $method, $arguments); } /** * @param \OC\Files\Storage\Storage $storage * @param string $mountPoint * @param array $arguments */ public function mount($storage, $mountPoint, $arguments = array()) { $mount = new MountPoint($storage, $mountPoint, $arguments); $this->mountManager->addMount($mount); } /** * @param string $mountPoint * @return \OC\Files\Mount\MountPoint */ public function getMount($mountPoint) { return $this->mountManager->find($mountPoint); } /** * @param string $mountPoint * @return \OC\Files\Mount\MountPoint[] */ public function getMountsIn($mountPoint) { return $this->mountManager->findIn($mountPoint); } /** * @param string $storageId * @return \OC\Files\Mount\MountPoint[] */ public function getMountByStorageId($storageId) { return $this->mountManager->findByStorageId($storageId); } /** * @param int $numericId * @return MountPoint[] */ public function getMountByNumericStorageId($numericId) { return $this->mountManager->findByNumericId($numericId); } /** * @param \OC\Files\Mount\MountPoint $mount */ public function unMount($mount) { $this->mountManager->remove($mount); } /** * @param string $path * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException * @return string */ public function get($path) { $path = $this->normalizePath($path); if ($this->isValidPath($path)) { $fullPath = $this->getFullPath($path); $fileInfo = $this->view->getFileInfo($fullPath); if ($fileInfo) { return $this->createNode($fullPath, $fileInfo); } else { throw new NotFoundException($path); } } else { throw new NotPermittedException(); } } //most operations can't be done on the root /** * @param string $targetPath * @throws \OCP\Files\NotPermittedException * @return \OC\Files\Node\Node */ public function rename($targetPath) { throw new NotPermittedException(); } public function delete() { throw new NotPermittedException(); } /** * @param string $targetPath * @throws \OCP\Files\NotPermittedException * @return \OC\Files\Node\Node */ public function copy($targetPath) { throw new NotPermittedException(); } /** * @param int $mtime * @throws \OCP\Files\NotPermittedException */ public function touch($mtime = null) { throw new NotPermittedException(); } /** * @return \OC\Files\Storage\Storage * @throws \OCP\Files\NotFoundException */ public function getStorage() { throw new NotFoundException(); } /** * @return string */ public function getPath() { return '/'; } /** * @return string */ public function getInternalPath() { return ''; } /** * @return int */ public function getId() { return null; } /** * @return array */ public function stat() { return null; } /** * @return int */ public function getMTime() { return null; } /** * @return int */ public function getSize() { return null; } /** * @return string */ public function getEtag() { return null; } /** * @return int */ public function getPermissions() { return \OCP\Constants::PERMISSION_CREATE; } /** * @return bool */ public function isReadable() { return false; } /** * @return bool */ public function isUpdateable() { return false; } /** * @return bool */ public function isDeletable() { return false; } /** * @return bool */ public function isShareable() { return false; } /** * @return Node * @throws \OCP\Files\NotFoundException */ public function getParent() { throw new NotFoundException(); } /** * @return string */ public function getName() { return ''; } /** * Returns a view to user's files folder * * @param String $userId user ID * @return \OCP\Files\Folder * @throws \OC\User\NoUserException */ public function getUserFolder($userId) { $userObject = $this->userManager->get($userId); if (is_null($userObject)) { $this->logger->error( sprintf( 'Backends provided no user object for %s', $userId ), [ 'app' => 'files', ] ); throw new \OC\User\NoUserException('Backends provided no user object'); } $userId = $userObject->getUID(); if (!$this->userFolderCache->hasKey($userId)) { \OC\Files\Filesystem::initMountPoints($userId); try { $folder = $this->get('/' . $userId . '/files'); } catch (NotFoundException $e) { if (!$this->nodeExists('/' . $userId)) { $this->newFolder('/' . $userId); } $folder = $this->newFolder('/' . $userId . '/files'); } $this->userFolderCache->set($userId, $folder); } return $this->userFolderCache->get($userId); } public function clearCache() { $this->userFolderCache = new CappedMemoryCache(); } public function getUserMountCache() { return $this->userMountCache; } } private/Files/Node/Node.php 0000604 00000023772 15247130453 0011556 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Files\Node; use OC\Files\Filesystem; use OCP\Files\FileInfo; use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; // FIXME: this class really should be abstract class Node implements \OCP\Files\Node { /** * @var \OC\Files\View $view */ protected $view; /** * @var \OC\Files\Node\Root $root */ protected $root; /** * @var string $path */ protected $path; /** * @var \OCP\Files\FileInfo */ protected $fileInfo; /** * @param \OC\Files\View $view * @param \OCP\Files\IRootFolder $root * @param string $path * @param FileInfo $fileInfo */ public function __construct($root, $view, $path, $fileInfo = null) { $this->view = $view; $this->root = $root; $this->path = $path; $this->fileInfo = $fileInfo; } /** * Creates a Node of the same type that represents a non-existing path * * @param string $path path * @return string non-existing node class */ protected function createNonExistingNode($path) { throw new \Exception('Must be implemented by subclasses'); } /** * Returns the matching file info * * @return FileInfo * @throws InvalidPathException * @throws NotFoundException */ public function getFileInfo() { if (!Filesystem::isValidPath($this->path)) { throw new InvalidPathException(); } if (!$this->fileInfo) { $fileInfo = $this->view->getFileInfo($this->path); if ($fileInfo instanceof FileInfo) { $this->fileInfo = $fileInfo; } else { throw new NotFoundException(); } } return $this->fileInfo; } /** * @param string[] $hooks */ protected function sendHooks($hooks) { foreach ($hooks as $hook) { $this->root->emit('\OC\Files', $hook, array($this)); } } /** * @param int $permissions * @return bool */ protected function checkPermissions($permissions) { return ($this->getPermissions() & $permissions) === $permissions; } public function delete() { return; } /** * @param int $mtime * @throws \OCP\Files\NotPermittedException */ public function touch($mtime = null) { if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) { $this->sendHooks(array('preTouch')); $this->view->touch($this->path, $mtime); $this->sendHooks(array('postTouch')); if ($this->fileInfo) { if (is_null($mtime)) { $mtime = time(); } $this->fileInfo['mtime'] = $mtime; } } else { throw new NotPermittedException(); } } /** * @return \OC\Files\Storage\Storage * @throws \OCP\Files\NotFoundException */ public function getStorage() { list($storage,) = $this->view->resolvePath($this->path); return $storage; } /** * @return string */ public function getPath() { return $this->path; } /** * @return string */ public function getInternalPath() { list(, $internalPath) = $this->view->resolvePath($this->path); return $internalPath; } /** * @return int * @throws InvalidPathException * @throws NotFoundException */ public function getId() { return $this->getFileInfo()->getId(); } /** * @return array */ public function stat() { return $this->view->stat($this->path); } /** * @return int * @throws InvalidPathException * @throws NotFoundException */ public function getMTime() { return $this->getFileInfo()->getMTime(); } /** * @return int * @throws InvalidPathException * @throws NotFoundException */ public function getSize() { return $this->getFileInfo()->getSize(); } /** * @return string * @throws InvalidPathException * @throws NotFoundException */ public function getEtag() { return $this->getFileInfo()->getEtag(); } /** * @return int * @throws InvalidPathException * @throws NotFoundException */ public function getPermissions() { return $this->getFileInfo()->getPermissions(); } /** * @return bool * @throws InvalidPathException * @throws NotFoundException */ public function isReadable() { return $this->getFileInfo()->isReadable(); } /** * @return bool * @throws InvalidPathException * @throws NotFoundException */ public function isUpdateable() { return $this->getFileInfo()->isUpdateable(); } /** * @return bool * @throws InvalidPathException * @throws NotFoundException */ public function isDeletable() { return $this->getFileInfo()->isDeletable(); } /** * @return bool * @throws InvalidPathException * @throws NotFoundException */ public function isShareable() { return $this->getFileInfo()->isShareable(); } /** * @return bool * @throws InvalidPathException * @throws NotFoundException */ public function isCreatable() { return $this->getFileInfo()->isCreatable(); } /** * @return Node */ public function getParent() { return $this->root->get(dirname($this->path)); } /** * @return string */ public function getName() { return basename($this->path); } /** * @param string $path * @return string */ protected function normalizePath($path) { if ($path === '' or $path === '/') { return '/'; } //no windows style slashes $path = str_replace('\\', '/', $path); //add leading slash if ($path[0] !== '/') { $path = '/' . $path; } //remove duplicate slashes while (strpos($path, '//') !== false) { $path = str_replace('//', '/', $path); } //remove trailing slash $path = rtrim($path, '/'); return $path; } /** * check if the requested path is valid * * @param string $path * @return bool */ public function isValidPath($path) { if (!$path || $path[0] !== '/') { $path = '/' . $path; } if (strstr($path, '/../') || strrchr($path, '/') === '/..') { return false; } return true; } public function isMounted() { return $this->getFileInfo()->isMounted(); } public function isShared() { return $this->getFileInfo()->isShared(); } public function getMimeType() { return $this->getFileInfo()->getMimetype(); } public function getMimePart() { return $this->getFileInfo()->getMimePart(); } public function getType() { return $this->getFileInfo()->getType(); } public function isEncrypted() { return $this->getFileInfo()->isEncrypted(); } public function getMountPoint() { return $this->getFileInfo()->getMountPoint(); } public function getOwner() { return $this->getFileInfo()->getOwner(); } public function getChecksum() { return; } /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException */ public function lock($type) { $this->view->lockFile($this->path, $type); } /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException */ public function changeLock($type) { $this->view->changeLock($this->path, $type); } /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE * @throws \OCP\Lock\LockedException */ public function unlock($type) { $this->view->unlockFile($this->path, $type); } /** * @param string $targetPath * @throws \OCP\Files\NotPermittedException if copy not allowed or failed * @return \OC\Files\Node\Node */ public function copy($targetPath) { $targetPath = $this->normalizePath($targetPath); $parent = $this->root->get(dirname($targetPath)); if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) { $nonExisting = $this->createNonExistingNode($targetPath); $this->root->emit('\OC\Files', 'preCopy', [$this, $nonExisting]); $this->root->emit('\OC\Files', 'preWrite', [$nonExisting]); if (!$this->view->copy($this->path, $targetPath)) { throw new NotPermittedException('Could not copy ' . $this->path . ' to ' . $targetPath); } $targetNode = $this->root->get($targetPath); $this->root->emit('\OC\Files', 'postCopy', [$this, $targetNode]); $this->root->emit('\OC\Files', 'postWrite', [$targetNode]); return $targetNode; } else { throw new NotPermittedException('No permission to copy to path ' . $targetPath); } } /** * @param string $targetPath * @throws \OCP\Files\NotPermittedException if move not allowed or failed * @return \OC\Files\Node\Node */ public function move($targetPath) { $targetPath = $this->normalizePath($targetPath); $parent = $this->root->get(dirname($targetPath)); if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) { $nonExisting = $this->createNonExistingNode($targetPath); $this->root->emit('\OC\Files', 'preRename', [$this, $nonExisting]); $this->root->emit('\OC\Files', 'preWrite', [$nonExisting]); if (!$this->view->rename($this->path, $targetPath)) { throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath); } $targetNode = $this->root->get($targetPath); $this->root->emit('\OC\Files', 'postRename', [$this, $targetNode]); $this->root->emit('\OC\Files', 'postWrite', [$targetNode]); $this->path = $targetPath; return $targetNode; } else { throw new NotPermittedException('No permission to move to path ' . $targetPath); } } } private/ServerContainer.php 0000604 00000007612 15247130453 0012046 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\AppFramework\App; use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Utility\SimpleContainer; use OCP\AppFramework\QueryException; /** * Class ServerContainer * * @package OC */ class ServerContainer extends SimpleContainer { /** @var DIContainer[] */ protected $appContainers; /** @var string[] */ protected $hasNoAppContainer; /** @var string[] */ protected $namespaces; /** * ServerContainer constructor. */ public function __construct() { parent::__construct(); $this->appContainers = []; $this->namespaces = []; $this->hasNoAppContainer = []; } /** * @param string $appName * @param string $appNamespace */ public function registerNamespace($appName, $appNamespace) { // Cut of OCA\ and lowercase $appNamespace = strtolower(substr($appNamespace, strrpos($appNamespace, '\\') + 1)); $this->namespaces[$appNamespace] = $appName; } /** * @param string $appName * @param DIContainer $container */ public function registerAppContainer($appName, DIContainer $container) { $this->appContainers[strtolower(App::buildAppNamespace($appName, ''))] = $container; } /** * @param string $namespace * @param string $sensitiveNamespace * @return DIContainer * @throws QueryException */ protected function getAppContainer($namespace, $sensitiveNamespace) { if (isset($this->appContainers[$namespace])) { return $this->appContainers[$namespace]; } if (isset($this->namespaces[$namespace])) { if (!isset($this->hasNoAppContainer[$namespace])) { $applicationClassName = 'OCA\\' . $sensitiveNamespace . '\\AppInfo\\Application'; if (class_exists($applicationClassName)) { new $applicationClassName(); if (isset($this->appContainers[$namespace])) { return $this->appContainers[$namespace]; } } $this->hasNoAppContainer[$namespace] = true; } return new DIContainer($this->namespaces[$namespace]); } throw new QueryException(); } /** * @param string $name name of the service to query for * @return mixed registered service for the given $name * @throws QueryException if the query could not be resolved */ public function query($name) { $name = $this->sanitizeName($name); // In case the service starts with OCA\ we try to find the service in // the apps container first. if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) { $segments = explode('\\', $name); try { $appContainer = $this->getAppContainer(strtolower($segments[1]), $segments[1]); return $appContainer->queryNoFallback($name); } catch (QueryException $e) { // Didn't find the service or the respective app container, // ignore it and fall back to the core container. } } else if (strpos($name, 'OC\\Settings\\') === 0 && substr_count($name, '\\') >= 3) { $segments = explode('\\', $name); try { $appContainer = $this->getAppContainer(strtolower($segments[1]), $segments[1]); return $appContainer->queryNoFallback($name); } catch (QueryException $e) { // Didn't find the service or the respective app container, // ignore it and fall back to the core container. } } return parent::query($name); } } private/L10N/LanguageNotFoundException.php 0000604 00000001551 15247130453 0014462 0 ustar 00 <?php /** * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\L10N; class LanguageNotFoundException extends \Exception { } private/L10N/Factory.php 0000604 00000026046 15247130453 0011020 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> * @copyright 2016 Lukas Reschke <lukas@statuscode.ch> * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\L10N; use OCP\IConfig; use OCP\IRequest; use OCP\IUserSession; use OCP\L10N\IFactory; /** * A factory that generates language instances */ class Factory implements IFactory { /** @var string */ protected $requestLanguage = ''; /** * cached instances * @var array Structure: Lang => App => \OCP\IL10N */ protected $instances = []; /** * @var array Structure: App => string[] */ protected $availableLanguages = []; /** * @var array Structure: string => callable */ protected $pluralFunctions = []; /** @var IConfig */ protected $config; /** @var IRequest */ protected $request; /** @var IUserSession */ protected $userSession; /** @var string */ protected $serverRoot; /** * @param IConfig $config * @param IRequest $request * @param IUserSession $userSession * @param string $serverRoot */ public function __construct(IConfig $config, IRequest $request, IUserSession $userSession, $serverRoot) { $this->config = $config; $this->request = $request; $this->userSession = $userSession; $this->serverRoot = $serverRoot; } /** * Get a language instance * * @param string $app * @param string|null $lang * @return \OCP\IL10N */ public function get($app, $lang = null) { $app = \OC_App::cleanAppId($app); if ($lang !== null) { $lang = str_replace(array('\0', '/', '\\', '..'), '', (string) $lang); } $forceLang = $this->config->getSystemValue('force_language', false); if (is_string($forceLang)) { $lang = $forceLang; } if ($lang === null || !$this->languageExists($app, $lang)) { $lang = $this->findLanguage($app); } if (!isset($this->instances[$lang][$app])) { $this->instances[$lang][$app] = new L10N( $this, $app, $lang, $this->getL10nFilesForApp($app, $lang) ); } return $this->instances[$lang][$app]; } /** * Find the best language * * @param string|null $app App id or null for core * @return string language If nothing works it returns 'en' */ public function findLanguage($app = null) { if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) { return $this->requestLanguage; } /** * At this point Nextcloud might not yet be installed and thus the lookup * in the preferences table might fail. For this reason we need to check * whether the instance has already been installed * * @link https://github.com/owncloud/core/issues/21955 */ if($this->config->getSystemValue('installed', false)) { $userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() : null; if(!is_null($userId)) { $userLang = $this->config->getUserValue($userId, 'core', 'lang', null); } else { $userLang = null; } } else { $userId = null; $userLang = null; } if ($userLang) { $this->requestLanguage = $userLang; if ($this->languageExists($app, $userLang)) { return $userLang; } } try { // Try to get the language from the Request $lang = $this->getLanguageFromRequest($app); if ($userId !== null && $app === null && !$userLang) { $this->config->setUserValue($userId, 'core', 'lang', $lang); } return $lang; } catch (LanguageNotFoundException $e) { // Finding language from request failed fall back to default language $defaultLanguage = $this->config->getSystemValue('default_language', false); if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) { return $defaultLanguage; } } // We could not find any language so fall back to english return 'en'; } /** * Find all available languages for an app * * @param string|null $app App id or null for core * @return array an array of available languages */ public function findAvailableLanguages($app = null) { $key = $app; if ($key === null) { $key = 'null'; } // also works with null as key if (!empty($this->availableLanguages[$key])) { return $this->availableLanguages[$key]; } $available = ['en']; //english is always available $dir = $this->findL10nDir($app); if (is_dir($dir)) { $files = scandir($dir); if ($files !== false) { foreach ($files as $file) { if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') { $available[] = substr($file, 0, -5); } } } } // merge with translations from theme $theme = $this->config->getSystemValue('theme'); if (!empty($theme)) { $themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot)); if (is_dir($themeDir)) { $files = scandir($themeDir); if ($files !== false) { foreach ($files as $file) { if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') { $available[] = substr($file, 0, -5); } } } } } $this->availableLanguages[$key] = $available; return $available; } /** * @param string|null $app App id or null for core * @param string $lang * @return bool */ public function languageExists($app, $lang) { if ($lang === 'en') {//english is always available return true; } $languages = $this->findAvailableLanguages($app); return array_search($lang, $languages) !== false; } /** * @param string|null $app * @return string * @throws LanguageNotFoundException */ private function getLanguageFromRequest($app) { $header = $this->request->getHeader('ACCEPT_LANGUAGE'); if ($header) { $available = $this->findAvailableLanguages($app); // E.g. make sure that 'de' is before 'de_DE'. sort($available); $preferences = preg_split('/,\s*/', strtolower($header)); foreach ($preferences as $preference) { list($preferred_language) = explode(';', $preference); $preferred_language = str_replace('-', '_', $preferred_language); foreach ($available as $available_language) { if ($preferred_language === strtolower($available_language)) { return $available_language; } } // Fallback from de_De to de foreach ($available as $available_language) { if (substr($preferred_language, 0, 2) === $available_language) { return $available_language; } } } } throw new LanguageNotFoundException(); } /** * Checks if $sub is a subdirectory of $parent * * @param string $sub * @param string $parent * @return bool */ private function isSubDirectory($sub, $parent) { // Check whether $sub contains no ".." if(strpos($sub, '..') !== false) { return false; } // Check whether $sub is a subdirectory of $parent if (strpos($sub, $parent) === 0) { return true; } return false; } /** * Get a list of language files that should be loaded * * @param string $app * @param string $lang * @return string[] */ // FIXME This method is only public, until OC_L10N does not need it anymore, // FIXME This is also the reason, why it is not in the public interface public function getL10nFilesForApp($app, $lang) { $languageFiles = []; $i18nDir = $this->findL10nDir($app); $transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json'; if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/') || $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/') || $this->isSubDirectory($transFile, $this->serverRoot . '/settings/l10n/') || $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/') ) && file_exists($transFile)) { // load the translations file $languageFiles[] = $transFile; } // merge with translations from theme $theme = $this->config->getSystemValue('theme'); if (!empty($theme)) { $transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot)); if (file_exists($transFile)) { $languageFiles[] = $transFile; } } return $languageFiles; } /** * find the l10n directory * * @param string $app App id or empty string for core * @return string directory */ protected function findL10nDir($app = null) { if (in_array($app, ['core', 'lib', 'settings'])) { if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) { return $this->serverRoot . '/' . $app . '/l10n/'; } } else if ($app && \OC_App::getAppPath($app) !== false) { // Check if the app is in the app folder return \OC_App::getAppPath($app) . '/l10n/'; } return $this->serverRoot . '/core/l10n/'; } /** * Creates a function from the plural string * * Parts of the code is copied from Habari: * https://github.com/habari/system/blob/master/classes/locale.php * @param string $string * @return string */ public function createPluralFunction($string) { if (isset($this->pluralFunctions[$string])) { return $this->pluralFunctions[$string]; } if (preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) { // sanitize $nplurals = preg_replace( '/[^0-9]/', '', $matches[1] ); $plural = preg_replace( '#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2] ); $body = str_replace( array( 'plural', 'n', '$n$plurals', ), array( '$plural', '$n', '$nplurals', ), 'nplurals='. $nplurals . '; plural=' . $plural ); // add parents // important since PHP's ternary evaluates from left to right $body .= ';'; $res = ''; $p = 0; for($i = 0; $i < strlen($body); $i++) { $ch = $body[$i]; switch ( $ch ) { case '?': $res .= ' ? ('; $p++; break; case ':': $res .= ') : ('; break; case ';': $res .= str_repeat( ')', $p ) . ';'; $p = 0; break; default: $res .= $ch; } } $body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);'; $function = create_function('$n', $body); $this->pluralFunctions[$string] = $function; return $function; } else { // default: one plural form for all cases but n==1 (english) $function = create_function( '$n', '$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);' ); $this->pluralFunctions[$string] = $function; return $function; } } } private/L10N/L10N.php 0000604 00000013702 15247130453 0010056 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\L10N; use OCP\IL10N; use OCP\L10N\IFactory; use Punic\Calendar; class L10N implements IL10N { /** @var IFactory */ protected $factory; /** @var string App of this object */ protected $app; /** @var string Language of this object */ protected $lang; /** @var string Plural forms (string) */ private $pluralFormString = 'nplurals=2; plural=(n != 1);'; /** @var string Plural forms (function) */ private $pluralFormFunction = null; /** @var string[] */ private $translations = []; /** * @param IFactory $factory * @param string $app * @param string $lang * @param array $files */ public function __construct(IFactory $factory, $app, $lang, array $files) { $this->factory = $factory; $this->app = $app; $this->lang = $lang; $this->translations = []; foreach ($files as $languageFile) { $this->load($languageFile); } } /** * The code (en, de, ...) of the language that is used for this instance * * @return string language */ public function getLanguageCode() { return $this->lang; } /** * Translating * @param string $text The text we need a translation for * @param array $parameters default:array() Parameters for sprintf * @return string Translation or the same text * * Returns the translation. If no translation is found, $text will be * returned. */ public function t($text, $parameters = array()) { return (string) new \OC_L10N_String($this, $text, $parameters); } /** * Translating * @param string $text_singular the string to translate for exactly one object * @param string $text_plural the string to translate for n objects * @param integer $count Number of objects * @param array $parameters default:array() Parameters for sprintf * @return string Translation or the same text * * Returns the translation. If no translation is found, $text will be * returned. %n will be replaced with the number of objects. * * The correct plural is determined by the plural_forms-function * provided by the po file. * */ public function n($text_singular, $text_plural, $count, $parameters = array()) { $identifier = "_${text_singular}_::_${text_plural}_"; if (isset($this->translations[$identifier])) { return (string) new \OC_L10N_String($this, $identifier, $parameters, $count); } else { if ($count === 1) { return (string) new \OC_L10N_String($this, $text_singular, $parameters, $count); } else { return (string) new \OC_L10N_String($this, $text_plural, $parameters, $count); } } } /** * Localization * @param string $type Type of localization * @param \DateTime|int|string $data parameters for this localization * @param array $options * @return string|int|false * * Returns the localized data. * * Implemented types: * - date * - Creates a date * - params: timestamp (int/string) * - datetime * - Creates date and time * - params: timestamp (int/string) * - time * - Creates a time * - params: timestamp (int/string) * - firstday: Returns the first day of the week (0 sunday - 6 saturday) * - jsdate: Returns the short JS date format */ public function l($type, $data = null, $options = array()) { // Use the language of the instance $locale = $this->getLanguageCode(); if ($locale === 'sr@latin') { $locale = 'sr_latn'; } if ($type === 'firstday') { return (int) Calendar::getFirstWeekday($locale); } if ($type === 'jsdate') { return (string) Calendar::getDateFormat('short', $locale); } $value = new \DateTime(); if ($data instanceof \DateTime) { $value = $data; } else if (is_string($data) && !is_numeric($data)) { $data = strtotime($data); $value->setTimestamp($data); } else if ($data !== null) { $value->setTimestamp($data); } $options = array_merge(array('width' => 'long'), $options); $width = $options['width']; switch ($type) { case 'date': return (string) Calendar::formatDate($value, $width, $locale); case 'datetime': return (string) Calendar::formatDatetime($value, $width, $locale); case 'time': return (string) Calendar::formatTime($value, $width, $locale); default: return false; } } /** * Returns an associative array with all translations * * Called by \OC_L10N_String * @return array */ public function getTranslations() { return $this->translations; } /** * Returnsed function accepts the argument $n * * Called by \OC_L10N_String * @return string the plural form function */ public function getPluralFormFunction() { if (is_null($this->pluralFormFunction)) { $this->pluralFormFunction = $this->factory->createPluralFunction($this->pluralFormString); } return $this->pluralFormFunction; } /** * @param $translationFile * @return bool */ protected function load($translationFile) { $json = json_decode(file_get_contents($translationFile), true); if (!is_array($json)) { $jsonError = json_last_error(); \OC::$server->getLogger()->warning("Failed to load $translationFile - json error code: $jsonError", ['app' => 'l10n']); return false; } if (!empty($json['pluralForm'])) { $this->pluralFormString = $json['pluralForm']; } $this->translations = array_merge($this->translations, $json['translations']); return true; } } private/ServiceUnavailableException.php 0000604 00000001546 15247130453 0014360 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class ServiceUnavailableException extends \Exception { } private/Route/Route.php 0000604 00000007444 15247130453 0011134 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author David Prévot <taffit@debian.org> * @author Felix Moeller <mail@felixmoeller.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Tanghus <thomas@tanghus.net> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Route; use OCP\Route\IRoute; use Symfony\Component\Routing\Route as SymfonyRoute; class Route extends SymfonyRoute implements IRoute { /** * Specify the method when this route is to be used * * @param string $method HTTP method (uppercase) * @return \OC\Route\Route */ public function method($method) { $this->setMethods($method); return $this; } /** * Specify POST as the method to use with this route * @return \OC\Route\Route */ public function post() { $this->method('POST'); return $this; } /** * Specify GET as the method to use with this route * @return \OC\Route\Route */ public function get() { $this->method('GET'); return $this; } /** * Specify PUT as the method to use with this route * @return \OC\Route\Route */ public function put() { $this->method('PUT'); return $this; } /** * Specify DELETE as the method to use with this route * @return \OC\Route\Route */ public function delete() { $this->method('DELETE'); return $this; } /** * Specify PATCH as the method to use with this route * @return \OC\Route\Route */ public function patch() { $this->method('PATCH'); return $this; } /** * Defaults to use for this route * * @param array $defaults The defaults * @return \OC\Route\Route */ public function defaults($defaults) { $action = $this->getDefault('action'); $this->setDefaults($defaults); if (isset($defaults['action'])) { $action = $defaults['action']; } $this->action($action); return $this; } /** * Requirements for this route * * @param array $requirements The requirements * @return \OC\Route\Route */ public function requirements($requirements) { $method = $this->getMethods(); $this->setRequirements($requirements); if (isset($requirements['_method'])) { $method = $requirements['_method']; } if ($method) { $this->method($method); } return $this; } /** * The action to execute when this route matches * * @param string|callable $class the class or a callable * @param string $function the function to use with the class * @return \OC\Route\Route * * This function is called with $class set to a callable or * to the class with $function */ public function action($class, $function = null) { $action = array($class, $function); if (is_null($function)) { $action = $class; } $this->setDefault('action', $action); return $this; } /** * The action to execute when this route matches, includes a file like * it is called directly * @param string $file * @return void */ public function actionInclude($file) { $function = create_function('$param', 'unset($param["_route"]);' .'$_GET=array_merge($_GET, $param);' .'unset($param);' .'require_once "'.$file.'";'); $this->action($function); } } private/Route/Router.php 0000604 00000026057 15247130453 0011317 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Felix Anand Epp <work@felixepp.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Route; use OCP\ILogger; use OCP\Route\IRouter; use OCP\AppFramework\App; use OCP\Util; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Exception\ResourceNotFoundException; class Router implements IRouter { /** @var RouteCollection[] */ protected $collections = []; /** @var null|RouteCollection */ protected $collection = null; /** @var null|string */ protected $collectionName = null; /** @var null|RouteCollection */ protected $root = null; /** @var null|UrlGenerator */ protected $generator = null; /** @var string[] */ protected $routingFiles; /** @var bool */ protected $loaded = false; /** @var array */ protected $loadedApps = []; /** @var ILogger */ protected $logger; /** @var RequestContext */ protected $context; /** * @param ILogger $logger */ public function __construct(ILogger $logger) { $this->logger = $logger; $baseUrl = \OC::$WEBROOT; if(!(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) { $baseUrl = \OC::$server->getURLGenerator()->linkTo('', 'index.php'); } if (!\OC::$CLI && isset($_SERVER['REQUEST_METHOD'])) { $method = $_SERVER['REQUEST_METHOD']; } else { $method = 'GET'; } $request = \OC::$server->getRequest(); $host = $request->getServerHost(); $schema = $request->getServerProtocol(); $this->context = new RequestContext($baseUrl, $method, $host, $schema); // TODO cache $this->root = $this->getCollection('root'); } /** * Get the files to load the routes from * * @return string[] */ public function getRoutingFiles() { if (!isset($this->routingFiles)) { $this->routingFiles = []; foreach (\OC_APP::getEnabledApps() as $app) { $appPath = \OC_App::getAppPath($app); if($appPath !== false) { $file = $appPath . '/appinfo/routes.php'; if (file_exists($file)) { $this->routingFiles[$app] = $file; } } } } return $this->routingFiles; } /** * Loads the routes * * @param null|string $app */ public function loadRoutes($app = null) { if(is_string($app)) { $app = \OC_App::cleanAppId($app); } $requestedApp = $app; if ($this->loaded) { return; } if (is_null($app)) { $this->loaded = true; $routingFiles = $this->getRoutingFiles(); } else { if (isset($this->loadedApps[$app])) { return; } $file = \OC_App::getAppPath($app) . '/appinfo/routes.php'; if ($file !== false && file_exists($file)) { $routingFiles = [$app => $file]; } else { $routingFiles = []; } } \OC::$server->getEventLogger()->start('loadroutes' . $requestedApp, 'Loading Routes'); foreach ($routingFiles as $app => $file) { if (!isset($this->loadedApps[$app])) { if (!\OC_App::isAppLoaded($app)) { // app MUST be loaded before app routes // try again next time loadRoutes() is called $this->loaded = false; continue; } $this->loadedApps[$app] = true; $this->useCollection($app); $this->requireRouteFile($file, $app); $collection = $this->getCollection($app); $collection->addPrefix('/apps/' . $app); $this->root->addCollection($collection); // Also add the OCS collection $collection = $this->getCollection($app.'.ocs'); $collection->addPrefix('/ocsapp'); $this->root->addCollection($collection); } } if (!isset($this->loadedApps['core'])) { $this->loadedApps['core'] = true; $this->useCollection('root'); require_once __DIR__ . '/../../../settings/routes.php'; require_once __DIR__ . '/../../../core/routes.php'; // Also add the OCS collection $collection = $this->getCollection('root.ocs'); $collection->addPrefix('/ocsapp'); $this->root->addCollection($collection); } if ($this->loaded) { // include ocs routes, must be loaded last for /ocs prefix require_once __DIR__ . '/../../../ocs/routes.php'; $collection = $this->getCollection('ocs'); $collection->addPrefix('/ocs'); $this->root->addCollection($collection); } \OC::$server->getEventLogger()->end('loadroutes' . $requestedApp); } /** * @return string * @deprecated */ public function getCacheKey() { return ''; } /** * @param string $name * @return \Symfony\Component\Routing\RouteCollection */ protected function getCollection($name) { if (!isset($this->collections[$name])) { $this->collections[$name] = new RouteCollection(); } return $this->collections[$name]; } /** * Sets the collection to use for adding routes * * @param string $name Name of the collection to use. * @return void */ public function useCollection($name) { $this->collection = $this->getCollection($name); $this->collectionName = $name; } /** * returns the current collection name in use for adding routes * * @return string the collection name */ public function getCurrentCollection() { return $this->collectionName; } /** * Create a \OC\Route\Route. * * @param string $name Name of the route to create. * @param string $pattern The pattern to match * @param array $defaults An array of default parameter values * @param array $requirements An array of requirements for parameters (regexes) * @return \OC\Route\Route */ public function create($name, $pattern, array $defaults = [], array $requirements = []) { $route = new Route($pattern, $defaults, $requirements); $this->collection->add($name, $route); return $route; } /** * Find the route matching $url * * @param string $url The url to find * @throws \Exception * @return void */ public function match($url) { if (substr($url, 0, 6) === '/apps/') { // empty string / 'apps' / $app / rest of the route list(, , $app,) = explode('/', $url, 4); $app = \OC_App::cleanAppId($app); \OC::$REQUESTEDAPP = $app; $this->loadRoutes($app); } else if (substr($url, 0, 13) === '/ocsapp/apps/') { // empty string / 'ocsapp' / 'apps' / $app / rest of the route list(, , , $app,) = explode('/', $url, 5); $app = \OC_App::cleanAppId($app); \OC::$REQUESTEDAPP = $app; $this->loadRoutes($app); } else if (substr($url, 0, 6) === '/core/' or substr($url, 0, 10) === '/settings/') { \OC::$REQUESTEDAPP = $url; if (!\OC::$server->getConfig()->getSystemValue('maintenance', false) && !Util::needUpgrade()) { \OC_App::loadApps(); } $this->loadRoutes('core'); } else { $this->loadRoutes(); } $matcher = new UrlMatcher($this->root, $this->context); try { $parameters = $matcher->match($url); } catch (ResourceNotFoundException $e) { if (substr($url, -1) !== '/') { // We allow links to apps/files? for backwards compatibility reasons // However, since Symfony does not allow empty route names, the route // we need to match is '/', so we need to append the '/' here. try { $parameters = $matcher->match($url . '/'); } catch (ResourceNotFoundException $newException) { // If we still didn't match a route, we throw the original exception throw $e; } } else { throw $e; } } \OC::$server->getEventLogger()->start('run_route', 'Run route'); if (isset($parameters['action'])) { $action = $parameters['action']; if (!is_callable($action)) { throw new \Exception('not a callable action'); } unset($parameters['action']); call_user_func($action, $parameters); } elseif (isset($parameters['file'])) { include $parameters['file']; } else { throw new \Exception('no action available'); } \OC::$server->getEventLogger()->end('run_route'); } /** * Get the url generator * * @return \Symfony\Component\Routing\Generator\UrlGenerator * */ public function getGenerator() { if (null !== $this->generator) { return $this->generator; } return $this->generator = new UrlGenerator($this->root, $this->context); } /** * Generate url based on $name and $parameters * * @param string $name Name of the route to use. * @param array $parameters Parameters for the route * @param bool $absolute * @return string */ public function generate($name, $parameters = [], $absolute = false) { $this->loadRoutes(); try { $referenceType = UrlGenerator::ABSOLUTE_URL; if ($absolute === false) { $referenceType = UrlGenerator::ABSOLUTE_PATH; } return $this->getGenerator()->generate($name, $parameters, $referenceType); } catch (RouteNotFoundException $e) { $this->logger->logException($e); return ''; } } /** * To isolate the variable scope used inside the $file it is required in it's own method * * @param string $file the route file location to include * @param string $appName */ private function requireRouteFile($file, $appName) { $this->setupRoutes(include_once $file, $appName); } /** * If a routes.php file returns an array, try to set up the application and * register the routes for the app. The application class will be chosen by * camelcasing the appname, e.g.: my_app will be turned into * \OCA\MyApp\AppInfo\Application. If that class does not exist, a default * App will be intialized. This makes it optional to ship an * appinfo/application.php by using the built in query resolver * * @param array $routes the application routes * @param string $appName the name of the app. */ private function setupRoutes($routes, $appName) { if (is_array($routes)) { $appNameSpace = App::buildAppNamespace($appName); $applicationClassName = $appNameSpace . '\\AppInfo\\Application'; if (class_exists($applicationClassName)) { $application = new $applicationClassName(); } else { $application = new App($appName); } $application->registerRoutes($this, $routes); } } } private/Route/CachingRouter.php 0000604 00000003511 15247130453 0012562 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Route; use OCP\ILogger; class CachingRouter extends Router { /** * @var \OCP\ICache */ protected $cache; /** * @param \OCP\ICache $cache * @param ILogger $logger */ public function __construct($cache, ILogger $logger) { $this->cache = $cache; parent::__construct($logger); } /** * Generate url based on $name and $parameters * * @param string $name Name of the route to use. * @param array $parameters Parameters for the route * @param bool $absolute * @return string */ public function generate($name, $parameters = array(), $absolute = false) { asort($parameters); $key = $this->context->getHost() . '#' . $this->context->getBaseUrl() . $name . sha1(json_encode($parameters)) . intval($absolute); $cachedKey = $this->cache->get($key); if ($cachedKey) { return $cachedKey; } else { $url = parent::generate($name, $parameters, $absolute); $this->cache->set($key, $url, 3600); return $url; } } } private/Streamer.php 0000604 00000007600 15247130453 0010514 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use ownCloud\TarStreamer\TarStreamer; use ZipStreamer\ZipStreamer; class Streamer { // array of regexp. Matching user agents will get tar instead of zip private $preferTarFor = [ '/macintosh|mac os x/i' ]; // streamer instance private $streamerInstance; public function __construct(){ /** @var \OCP\IRequest */ $request = \OC::$server->getRequest(); if ($request->isUserAgent($this->preferTarFor)) { $this->streamerInstance = new TarStreamer(); } else { $this->streamerInstance = new ZipStreamer(['zip64' => PHP_INT_SIZE !== 4]); } } /** * Send HTTP headers * @param string $name */ public function sendHeaders($name){ $extension = $this->streamerInstance instanceof ZipStreamer ? '.zip' : '.tar'; $fullName = $name . $extension; $this->streamerInstance->sendHeaders($fullName); } /** * Stream directory recursively * @param string $dir * @param string $internalDir */ public function addDirRecursive($dir, $internalDir='') { $dirname = basename($dir); $rootDir = $internalDir . $dirname; if (!empty($rootDir)) { $this->streamerInstance->addEmptyDir($rootDir); } $internalDir .= $dirname . '/'; // prevent absolute dirs $internalDir = ltrim($internalDir, '/'); $files= \OC\Files\Filesystem::getDirectoryContent($dir); foreach($files as $file) { $filename = $file['name']; $file = $dir . '/' . $filename; if(\OC\Files\Filesystem::is_file($file)) { $filesize = \OC\Files\Filesystem::filesize($file); $fileTime = \OC\Files\Filesystem::filemtime($file); $fh = \OC\Files\Filesystem::fopen($file, 'r'); $this->addFileFromStream($fh, $internalDir . $filename, $filesize, $fileTime); fclose($fh); }elseif(\OC\Files\Filesystem::is_dir($file)) { $this->addDirRecursive($file, $internalDir); } } } /** * Add a file to the archive at the specified location and file name. * * @param string $stream Stream to read data from * @param string $internalName Filepath and name to be used in the archive. * @param int $size Filesize * @param int|bool $time File mtime as int, or false * @return bool $success */ public function addFileFromStream($stream, $internalName, $size, $time) { $options = []; if ($time) { $options = [ 'timestamp' => $time ]; } if ($this->streamerInstance instanceof ZipStreamer) { return $this->streamerInstance->addFileFromStream($stream, $internalName, $options); } else { return $this->streamerInstance->addFileFromStream($stream, $internalName, $size, $options); } } /** * Add an empty directory entry to the archive. * * @param string $dirName Directory Path and name to be added to the archive. * @return bool $success */ public function addEmptyDir($dirName){ return $this->streamerInstance->addEmptyDir($dirName); } /** * Close the archive. * A closed archive can no longer have new files added to it. After * closing, the file is completely written to the output stream. * @return bool $success */ public function finalize(){ return $this->streamerInstance->finalize(); } } private/Migration/ConsoleOutput.php 0000604 00000004362 15247130453 0013510 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2017, ownCloud GmbH * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Migration; use OCP\Migration\IOutput; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Output\OutputInterface; /** * Class SimpleOutput * * Just a simple IOutput implementation with writes messages to the log file. * Alternative implementations will write to the console or to the web ui (web update case) * * @package OC\Migration */ class ConsoleOutput implements IOutput { /** @var OutputInterface */ private $output; /** @var ProgressBar */ private $progressBar; public function __construct(OutputInterface $output) { $this->output = $output; } /** * @param string $message */ public function info($message) { $this->output->writeln("<info>$message</info>"); } /** * @param string $message */ public function warning($message) { $this->output->writeln("<comment>$message</comment>"); } /** * @param int $max */ public function startProgress($max = 0) { if (!is_null($this->progressBar)) { $this->progressBar->finish(); } $this->progressBar = new ProgressBar($this->output); $this->progressBar->start($max); } /** * @param int $step * @param string $description */ public function advance($step = 1, $description = '') { if (!is_null($this->progressBar)) { $this->progressBar = new ProgressBar($this->output); $this->progressBar->start(); } $this->progressBar->advance($step); } public function finishProgress() { if (is_null($this->progressBar)) { return; } $this->progressBar->finish(); } } private/Migration/BackgroundRepair.php 0000604 00000005444 15247130453 0014111 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Migration; use OC\BackgroundJob\JobList; use OC\BackgroundJob\TimedJob; use OC\NeedsUpdateException; use OC\Repair; use OC_App; use OCP\BackgroundJob\IJobList; use OCP\ILogger; use Symfony\Component\EventDispatcher\EventDispatcher; /** * Class BackgroundRepair * * @package OC\Migration */ class BackgroundRepair extends TimedJob { /** @var IJobList */ private $jobList; /** @var ILogger */ private $logger; /** @var EventDispatcher */ private $dispatcher; public function setDispatcher(EventDispatcher $dispatcher) { $this->dispatcher = $dispatcher; } /** * run the job, then remove it from the job list * * @param JobList $jobList * @param ILogger $logger */ public function execute($jobList, ILogger $logger = null) { // add an interval of 15 mins $this->setInterval(15*60); $this->jobList = $jobList; $this->logger = $logger; parent::execute($jobList, $logger); } /** * @param array $argument * @throws \Exception * @throws \OC\NeedsUpdateException */ protected function run($argument) { if (!isset($argument['app']) || !isset($argument['step'])) { // remove the job - we can never execute it $this->jobList->remove($this, $this->argument); return; } $app = $argument['app']; try { $this->loadApp($app); } catch (NeedsUpdateException $ex) { // as long as the app is not yet done with it's offline migration // we better not start with the live migration return; } $step = $argument['step']; $repair = new Repair([], $this->dispatcher); try { $repair->addStep($step); } catch (\Exception $ex) { $this->logger->logException($ex,[ 'app' => 'migration' ]); // remove the job - we can never execute it $this->jobList->remove($this, $this->argument); return; } // execute the repair step $repair->run(); // remove the job once executed successfully $this->jobList->remove($this, $this->argument); } /** * @codeCoverageIgnore * @param $app * @throws NeedsUpdateException */ protected function loadApp($app) { OC_App::loadApp($app); } } private/AllConfig.php 0000604 00000033670 15247130453 0010576 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\Cache\CappedMemoryCache; use OCP\IDBConnection; use OCP\PreConditionNotMetException; /** * Class to combine all the configuration options ownCloud offers */ class AllConfig implements \OCP\IConfig { /** @var SystemConfig */ private $systemConfig; /** @var IDBConnection */ private $connection; /** * 3 dimensional array with the following structure: * [ $userId => * [ $appId => * [ $key => $value ] * ] * ] * * database table: preferences * * methods that use this: * - setUserValue * - getUserValue * - getUserKeys * - deleteUserValue * - deleteAllUserValues * - deleteAppFromAllUsers * * @var CappedMemoryCache $userCache */ private $userCache; /** * @param SystemConfig $systemConfig */ public function __construct(SystemConfig $systemConfig) { $this->userCache = new CappedMemoryCache(); $this->systemConfig = $systemConfig; } /** * TODO - FIXME This fixes an issue with base.php that cause cyclic * dependencies, especially with autoconfig setup * * Replace this by properly injected database connection. Currently the * base.php triggers the getDatabaseConnection too early which causes in * autoconfig setup case a too early distributed database connection and * the autoconfig then needs to reinit all already initialized dependencies * that use the database connection. * * otherwise a SQLite database is created in the wrong directory * because the database connection was created with an uninitialized config */ private function fixDIInit() { if($this->connection === null) { $this->connection = \OC::$server->getDatabaseConnection(); } } /** * Sets and deletes system wide values * * @param array $configs Associative array with `key => value` pairs * If value is null, the config key will be deleted */ public function setSystemValues(array $configs) { $this->systemConfig->setValues($configs); } /** * Sets a new system wide value * * @param string $key the key of the value, under which will be saved * @param mixed $value the value that should be stored */ public function setSystemValue($key, $value) { $this->systemConfig->setValue($key, $value); } /** * Looks up a system wide defined value * * @param string $key the key of the value, under which it was saved * @param mixed $default the default value to be returned if the value isn't set * @return mixed the value or $default */ public function getSystemValue($key, $default = '') { return $this->systemConfig->getValue($key, $default); } /** * Looks up a system wide defined value and filters out sensitive data * * @param string $key the key of the value, under which it was saved * @param mixed $default the default value to be returned if the value isn't set * @return mixed the value or $default */ public function getFilteredSystemValue($key, $default = '') { return $this->systemConfig->getFilteredValue($key, $default); } /** * Delete a system wide defined value * * @param string $key the key of the value, under which it was saved */ public function deleteSystemValue($key) { $this->systemConfig->deleteValue($key); } /** * Get all keys stored for an app * * @param string $appName the appName that we stored the value under * @return string[] the keys stored for the app */ public function getAppKeys($appName) { return \OC::$server->getAppConfig()->getKeys($appName); } /** * Writes a new app wide value * * @param string $appName the appName that we want to store the value under * @param string $key the key of the value, under which will be saved * @param string|float|int $value the value that should be stored */ public function setAppValue($appName, $key, $value) { \OC::$server->getAppConfig()->setValue($appName, $key, $value); } /** * Looks up an app wide defined value * * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @param string $default the default value to be returned if the value isn't set * @return string the saved value */ public function getAppValue($appName, $key, $default = '') { return \OC::$server->getAppConfig()->getValue($appName, $key, $default); } /** * Delete an app wide defined value * * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved */ public function deleteAppValue($appName, $key) { \OC::$server->getAppConfig()->deleteKey($appName, $key); } /** * Removes all keys in appconfig belonging to the app * * @param string $appName the appName the configs are stored under */ public function deleteAppValues($appName) { \OC::$server->getAppConfig()->deleteApp($appName); } /** * Set a user defined value * * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we want to store the value under * @param string $key the key under which the value is being stored * @param string|float|int $value the value that you want to store * @param string $preCondition only update if the config value was previously the value passed as $preCondition * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met * @throws \UnexpectedValueException when trying to store an unexpected value */ public function setUserValue($userId, $appName, $key, $value, $preCondition = null) { if (!is_int($value) && !is_float($value) && !is_string($value)) { throw new \UnexpectedValueException('Only integers, floats and strings are allowed as value'); } // TODO - FIXME $this->fixDIInit(); $prevValue = $this->getUserValue($userId, $appName, $key, null); if ($prevValue !== null) { if ($prevValue === (string)$value) { return; } else if ($preCondition !== null && $prevValue !== (string)$preCondition) { throw new PreConditionNotMetException(); } else { $qb = $this->connection->getQueryBuilder(); $qb->update('preferences') ->set('configvalue', $qb->createNamedParameter($value)) ->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId))) ->andWhere($qb->expr()->eq('appid', $qb->createNamedParameter($appName))) ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key))); $qb->execute(); $this->userCache[$userId][$appName][$key] = $value; return; } } $preconditionArray = []; if (isset($preCondition)) { $preconditionArray = [ 'configvalue' => $preCondition, ]; } $this->connection->setValues('preferences', [ 'userid' => $userId, 'appid' => $appName, 'configkey' => $key, ], [ 'configvalue' => $value, ], $preconditionArray); // only add to the cache if we already loaded data for the user if (isset($this->userCache[$userId])) { if (!isset($this->userCache[$userId][$appName])) { $this->userCache[$userId][$appName] = array(); } $this->userCache[$userId][$appName][$key] = $value; } } /** * Getting a user defined value * * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we stored the value under * @param string $key the key under which the value is being stored * @param mixed $default the default value to be returned if the value isn't set * @return string */ public function getUserValue($userId, $appName, $key, $default = '') { $data = $this->getUserValues($userId); if (isset($data[$appName]) and isset($data[$appName][$key])) { return $data[$appName][$key]; } else { return $default; } } /** * Get the keys of all stored by an app for the user * * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we stored the value under * @return string[] */ public function getUserKeys($userId, $appName) { $data = $this->getUserValues($userId); if (isset($data[$appName])) { return array_keys($data[$appName]); } else { return array(); } } /** * Delete a user value * * @param string $userId the userId of the user that we want to store the value under * @param string $appName the appName that we stored the value under * @param string $key the key under which the value is being stored */ public function deleteUserValue($userId, $appName, $key) { // TODO - FIXME $this->fixDIInit(); $sql = 'DELETE FROM `*PREFIX*preferences` '. 'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?'; $this->connection->executeUpdate($sql, array($userId, $appName, $key)); if (isset($this->userCache[$userId]) and isset($this->userCache[$userId][$appName])) { unset($this->userCache[$userId][$appName][$key]); } } /** * Delete all user values * * @param string $userId the userId of the user that we want to remove all values from */ public function deleteAllUserValues($userId) { // TODO - FIXME $this->fixDIInit(); $sql = 'DELETE FROM `*PREFIX*preferences` '. 'WHERE `userid` = ?'; $this->connection->executeUpdate($sql, array($userId)); unset($this->userCache[$userId]); } /** * Delete all user related values of one app * * @param string $appName the appName of the app that we want to remove all values from */ public function deleteAppFromAllUsers($appName) { // TODO - FIXME $this->fixDIInit(); $sql = 'DELETE FROM `*PREFIX*preferences` '. 'WHERE `appid` = ?'; $this->connection->executeUpdate($sql, array($appName)); foreach ($this->userCache as &$userCache) { unset($userCache[$appName]); } } /** * Returns all user configs sorted by app of one user * * @param string $userId the user ID to get the app configs from * @return array[] - 2 dimensional array with the following structure: * [ $appId => * [ $key => $value ] * ] */ private function getUserValues($userId) { if (isset($this->userCache[$userId])) { return $this->userCache[$userId]; } if ($userId === null || $userId === '') { $this->userCache[$userId]=array(); return $this->userCache[$userId]; } // TODO - FIXME $this->fixDIInit(); $data = array(); $query = 'SELECT `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?'; $result = $this->connection->executeQuery($query, array($userId)); while ($row = $result->fetch()) { $appId = $row['appid']; if (!isset($data[$appId])) { $data[$appId] = array(); } $data[$appId][$row['configkey']] = $row['configvalue']; } $this->userCache[$userId] = $data; return $data; } /** * Fetches a mapped list of userId -> value, for a specified app and key and a list of user IDs. * * @param string $appName app to get the value for * @param string $key the key to get the value for * @param array $userIds the user IDs to fetch the values for * @return array Mapped values: userId => value */ public function getUserValueForUsers($appName, $key, $userIds) { // TODO - FIXME $this->fixDIInit(); if (empty($userIds) || !is_array($userIds)) { return array(); } $chunkedUsers = array_chunk($userIds, 50, true); $placeholders50 = implode(',', array_fill(0, 50, '?')); $userValues = array(); foreach ($chunkedUsers as $chunk) { $queryParams = $chunk; // create [$app, $key, $chunkedUsers] array_unshift($queryParams, $key); array_unshift($queryParams, $appName); $placeholders = (sizeof($chunk) == 50) ? $placeholders50 : implode(',', array_fill(0, sizeof($chunk), '?')); $query = 'SELECT `userid`, `configvalue` ' . 'FROM `*PREFIX*preferences` ' . 'WHERE `appid` = ? AND `configkey` = ? ' . 'AND `userid` IN (' . $placeholders . ')'; $result = $this->connection->executeQuery($query, $queryParams); while ($row = $result->fetch()) { $userValues[$row['userid']] = $row['configvalue']; } } return $userValues; } /** * Determines the users that have the given value set for a specific app-key-pair * * @param string $appName the app to get the user for * @param string $key the key to get the user for * @param string $value the value to get the user for * @return array of user IDs */ public function getUsersForUserValue($appName, $key, $value) { // TODO - FIXME $this->fixDIInit(); $sql = 'SELECT `userid` FROM `*PREFIX*preferences` ' . 'WHERE `appid` = ? AND `configkey` = ? '; if($this->getSystemValue('dbtype', 'sqlite') === 'oci') { //oracle hack: need to explicitly cast CLOB to CHAR for comparison $sql .= 'AND to_char(`configvalue`) = ?'; } else { $sql .= 'AND `configvalue` = ?'; } $result = $this->connection->executeQuery($sql, array($appName, $key, $value)); $userIDs = array(); while ($row = $result->fetch()) { $userIDs[] = $row['userid']; } return $userIDs; } public function getSystemConfig() { return $this->systemConfig; } } private/DB/PgSqlTools.php 0000604 00000004365 15247130453 0011273 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Morris Jobke <hey@morrisjobke.de> * @author tbelau666 <thomas.belau@gmx.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use OCP\IConfig; /** * Various PostgreSQL specific helper functions. */ class PgSqlTools { /** @var \OCP\IConfig */ private $config; /** * @param \OCP\IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * @brief Resynchronizes all sequences of a database after using INSERTs * without leaving out the auto-incremented column. * @param \OC\DB\Connection $conn * @return null */ public function resynchronizeDatabaseSequences(Connection $conn) { $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; $databaseName = $conn->getDatabase(); $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); foreach ($conn->getSchemaManager()->listSequences() as $sequence) { $sequenceName = $sequence->getName(); $sqlInfo = 'SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE column_default = ? AND table_catalog = ?'; $sequenceInfo = $conn->fetchAssoc($sqlInfo, array( "nextval('$sequenceName'::regclass)", $databaseName )); $tableName = $sequenceInfo['table_name']; $columnName = $sequenceInfo['column_name']; $sqlMaxId = "SELECT MAX($columnName) FROM $tableName"; $sqlSetval = "SELECT setval('$sequenceName', ($sqlMaxId))"; $conn->executeQuery($sqlSetval); } } } private/DB/OracleMigrator.php 0000604 00000005003 15247130453 0012124 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\Schema\ColumnDiff; use Doctrine\DBAL\Schema\Schema; class OracleMigrator extends NoCheckMigrator { /** * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { $schemaDiff = parent::getDiff($targetSchema, $connection); // oracle forces us to quote the identifiers foreach ($schemaDiff->changedTables as $tableDiff) { $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); foreach ($tableDiff->changedColumns as $column) { $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); // auto increment is not relevant for oracle and can anyhow not be applied on change $column->changedProperties = array_diff($column->changedProperties, ['autoincrement', 'unsigned']); } $tableDiff->changedColumns = array_filter($tableDiff->changedColumns, function (ColumnDiff $column) { return count($column->changedProperties) > 0; }); } return $schemaDiff; } /** * @param string $name * @return string */ protected function generateTemporaryTableName($name) { return 'oc_' . uniqid(); } /** * @param $statement * @return string */ protected function convertStatementToScript($statement) { if (substr($statement, -1) === ';') { return $statement . PHP_EOL . '/' . PHP_EOL; } $script = $statement . ';'; $script .= PHP_EOL; $script .= PHP_EOL; return $script; } protected function getFilterExpression() { return '/^"' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; } } private/DB/AdapterPgSql.php 0000604 00000002363 15247130453 0011547 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; class AdapterPgSql extends Adapter { public function lastInsertId($table) { return $this->conn->fetchColumn('SELECT lastval()'); } const UNIX_TIMESTAMP_REPLACEMENT = 'cast(extract(epoch from current_timestamp) as integer)'; public function fixupStatement($statement) { $statement = str_replace( '`', '"', $statement ); $statement = str_ireplace( 'UNIX_TIMESTAMP()', self::UNIX_TIMESTAMP_REPLACEMENT, $statement ); return $statement; } } private/DB/AdapterOCI8.php 0000604 00000003413 15247130453 0011220 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; class AdapterOCI8 extends Adapter { public function lastInsertId($table) { if (is_null($table)) { throw new \InvalidArgumentException('Oracle requires a table name to be passed into lastInsertId()'); } if ($table !== null) { $suffix = '_SEQ'; $table = '"' . $table . $suffix . '"'; } return $this->conn->realLastInsertId($table); } const UNIX_TIMESTAMP_REPLACEMENT = "(cast(sys_extract_utc(systimestamp) as date) - date'1970-01-01') * 86400"; public function fixupStatement($statement) { $statement = preg_replace('/`(\w+)` ILIKE \?/', 'REGEXP_LIKE(`$1`, \'^\' || REPLACE(?, \'%\', \'.*\') || \'$\', \'i\')', $statement); $statement = str_replace('`', '"', $statement); $statement = str_ireplace('NOW()', 'CURRENT_TIMESTAMP', $statement); $statement = str_ireplace('UNIX_TIMESTAMP()', self::UNIX_TIMESTAMP_REPLACEMENT, $statement); return $statement; } } private/DB/MDB2SchemaReader.php 0000604 00000021332 15247130453 0012145 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Oliver Gasser <oliver.gasser@gmail.com> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\Schema; use OCP\IConfig; class MDB2SchemaReader { /** * @var string $DBTABLEPREFIX */ protected $DBTABLEPREFIX; /** * @var \Doctrine\DBAL\Platforms\AbstractPlatform $platform */ protected $platform; /** @var IConfig */ protected $config; /** * @param \OCP\IConfig $config * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform */ public function __construct(IConfig $config, AbstractPlatform $platform) { $this->platform = $platform; $this->config = $config; $this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_'); } /** * @param string $file * @param Schema $schema * @return Schema * @throws \DomainException */ public function loadSchemaFromFile($file, Schema $schema) { $loadEntities = libxml_disable_entity_loader(false); $xml = simplexml_load_file($file); libxml_disable_entity_loader($loadEntities); foreach ($xml->children() as $child) { /** * @var \SimpleXMLElement $child */ switch ($child->getName()) { case 'name': case 'create': case 'overwrite': case 'charset': break; case 'table': $this->loadTable($schema, $child); break; default: throw new \DomainException('Unknown element: ' . $child->getName()); } } return $schema; } /** * @param \Doctrine\DBAL\Schema\Schema $schema * @param \SimpleXMLElement $xml * @throws \DomainException */ private function loadTable($schema, $xml) { $table = null; foreach ($xml->children() as $child) { /** * @var \SimpleXMLElement $child */ switch ($child->getName()) { case 'name': $name = (string)$child; $name = str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name); $name = $this->platform->quoteIdentifier($name); $table = $schema->createTable($name); break; case 'create': case 'overwrite': case 'charset': break; case 'declaration': if (is_null($table)) { throw new \DomainException('Table declaration before table name'); } $this->loadDeclaration($table, $child); break; default: throw new \DomainException('Unknown element: ' . $child->getName()); } } } /** * @param \Doctrine\DBAL\Schema\Table $table * @param \SimpleXMLElement $xml * @throws \DomainException */ private function loadDeclaration($table, $xml) { foreach ($xml->children() as $child) { /** * @var \SimpleXMLElement $child */ switch ($child->getName()) { case 'field': $this->loadField($table, $child); break; case 'index': $this->loadIndex($table, $child); break; default: throw new \DomainException('Unknown element: ' . $child->getName()); } } } /** * @param \Doctrine\DBAL\Schema\Table $table * @param \SimpleXMLElement $xml * @throws \DomainException */ private function loadField($table, $xml) { $options = array( 'notnull' => false ); foreach ($xml->children() as $child) { /** * @var \SimpleXMLElement $child */ switch ($child->getName()) { case 'name': $name = (string)$child; $name = $this->platform->quoteIdentifier($name); break; case 'type': $type = (string)$child; switch ($type) { case 'text': $type = 'string'; break; case 'clob': $type = 'text'; break; case 'timestamp': $type = 'datetime'; break; case 'numeric': $type = 'decimal'; break; } break; case 'length': $length = (string)$child; $options['length'] = $length; break; case 'unsigned': $unsigned = $this->asBool($child); $options['unsigned'] = $unsigned; break; case 'notnull': $notnull = $this->asBool($child); $options['notnull'] = $notnull; break; case 'autoincrement': $autoincrement = $this->asBool($child); $options['autoincrement'] = $autoincrement; break; case 'default': $default = (string)$child; $options['default'] = $default; break; case 'comments': $comment = (string)$child; $options['comment'] = $comment; break; case 'primary': $primary = $this->asBool($child); $options['primary'] = $primary; break; case 'precision': $precision = (string)$child; $options['precision'] = $precision; break; case 'scale': $scale = (string)$child; $options['scale'] = $scale; break; default: throw new \DomainException('Unknown element: ' . $child->getName()); } } if (isset($name) && isset($type)) { if (isset($options['default']) && empty($options['default'])) { if (empty($options['notnull']) || !$options['notnull']) { unset($options['default']); $options['notnull'] = false; } else { $options['default'] = ''; } if ($type == 'integer' || $type == 'decimal') { $options['default'] = 0; } elseif ($type == 'boolean') { $options['default'] = false; } if (!empty($options['autoincrement']) && $options['autoincrement']) { unset($options['default']); } } if ($type === 'integer' && isset($options['default'])) { $options['default'] = (int)$options['default']; } if ($type === 'integer' && isset($options['length'])) { $length = $options['length']; if ($length < 4) { $type = 'smallint'; } else if ($length > 4) { $type = 'bigint'; } } if ($type === 'boolean' && isset($options['default'])) { $options['default'] = $this->asBool($options['default']); } if (!empty($options['autoincrement']) && !empty($options['notnull']) ) { $options['primary'] = true; } $table->addColumn($name, $type, $options); if (!empty($options['primary']) && $options['primary']) { $table->setPrimaryKey(array($name)); } } } /** * @param \Doctrine\DBAL\Schema\Table $table * @param \SimpleXMLElement $xml * @throws \DomainException */ private function loadIndex($table, $xml) { $name = null; $fields = array(); foreach ($xml->children() as $child) { /** * @var \SimpleXMLElement $child */ switch ($child->getName()) { case 'name': $name = (string)$child; break; case 'primary': $primary = $this->asBool($child); break; case 'unique': $unique = $this->asBool($child); break; case 'field': foreach ($child->children() as $field) { /** * @var \SimpleXMLElement $field */ switch ($field->getName()) { case 'name': $field_name = (string)$field; $field_name = $this->platform->quoteIdentifier($field_name); $fields[] = $field_name; break; case 'sorting': break; default: throw new \DomainException('Unknown element: ' . $field->getName()); } } break; default: throw new \DomainException('Unknown element: ' . $child->getName()); } } if (!empty($fields)) { if (isset($primary) && $primary) { if ($table->hasPrimaryKey()) { return; } $table->setPrimaryKey($fields, $name); } else { if (isset($unique) && $unique) { $table->addUniqueIndex($fields, $name); } else { $table->addIndex($fields, $name); } } } else { throw new \DomainException('Empty index definition: ' . $name . ' options:' . print_r($fields, true)); } } /** * @param \SimpleXMLElement|string $xml * @return bool */ private function asBool($xml) { $result = (string)$xml; if ($result == 'true') { $result = true; } elseif ($result == 'false') { $result = false; } return (bool)$result; } } private/DB/AdapterSqlite.php 0000604 00000005630 15247130453 0011762 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; class AdapterSqlite extends Adapter { /** * @param string $tableName */ public function lockTable($tableName) { $this->conn->executeUpdate('BEGIN EXCLUSIVE TRANSACTION'); } public function unlockTable() { $this->conn->executeUpdate('COMMIT TRANSACTION'); } public function fixupStatement($statement) { $statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement); $statement = str_replace( '`', '"', $statement ); $statement = str_ireplace( 'NOW()', 'datetime(\'now\')', $statement ); $statement = str_ireplace('GREATEST(', 'MAX(', $statement); $statement = str_ireplace( 'UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $statement ); return $statement; } /** * Insert a row if the matching row does not exists. * * @param string $table The table name (will replace *PREFIX* with the actual prefix) * @param array $input data that should be inserted into the table (column name => value) * @param array|null $compare List of values that should be checked for "if not exists" * If this is null or an empty array, all keys of $input will be compared * Please note: text fields (clob) must not be used in the compare array * @return int number of inserted rows * @throws \Doctrine\DBAL\DBALException */ public function insertIfNotExist($table, $input, array $compare = null) { if (empty($compare)) { $compare = array_keys($input); } $fieldList = '`' . implode('`,`', array_keys($input)) . '`'; $query = "INSERT INTO `$table` ($fieldList) SELECT " . str_repeat('?,', count($input)-1).'? ' . " WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE "; $inserts = array_values($input); foreach($compare as $key) { $query .= '`' . $key . '`'; if (is_null($input[$key])) { $query .= ' IS NULL AND '; } else { $inserts[] = $input[$key]; $query .= ' = ? AND '; } } $query = substr($query, 0, strlen($query) - 5); $query .= ')'; return $this->conn->executeUpdate($query, $inserts); } } private/DB/AdapterMySQL.php 0000604 00000003046 15247130453 0011465 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; class AdapterMySQL extends Adapter { /** @var string */ protected $charset; /** * @param string $tableName */ public function lockTable($tableName) { $this->conn->executeUpdate('LOCK TABLES `' .$tableName . '` WRITE'); } public function unlockTable() { $this->conn->executeUpdate('UNLOCK TABLES'); } public function fixupStatement($statement) { $statement = str_replace(' ILIKE ', ' COLLATE ' . $this->getCharset() . '_general_ci LIKE ', $statement); return $statement; } protected function getCharset() { if (!$this->charset) { $params = $this->conn->getParams(); $this->charset = isset($params['charset']) ? $params['charset'] : 'utf8'; } return $this->charset; } } private/DB/ConnectionFactory.php 0000604 00000016666 15247130453 0012662 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\Common\EventManager; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Event\Listeners\OracleSessionInit; use Doctrine\DBAL\Event\Listeners\SQLSessionInit; use OC\SystemConfig; /** * Takes care of creating and configuring Doctrine connections. */ class ConnectionFactory { /** * @var array * * Array mapping DBMS type to default connection parameters passed to * \Doctrine\DBAL\DriverManager::getConnection(). */ protected $defaultConnectionParams = [ 'mysql' => [ 'adapter' => '\OC\DB\AdapterMySQL', 'charset' => 'UTF8', 'driver' => 'pdo_mysql', 'wrapperClass' => 'OC\DB\Connection', ], 'oci' => [ 'adapter' => '\OC\DB\AdapterOCI8', 'charset' => 'AL32UTF8', 'driver' => 'oci8', 'wrapperClass' => 'OC\DB\OracleConnection', ], 'pgsql' => [ 'adapter' => '\OC\DB\AdapterPgSql', 'driver' => 'pdo_pgsql', 'wrapperClass' => 'OC\DB\Connection', ], 'sqlite3' => [ 'adapter' => '\OC\DB\AdapterSqlite', 'driver' => 'pdo_sqlite', 'wrapperClass' => 'OC\DB\Connection', ], ]; /** @var SystemConfig */ private $config; /** * ConnectionFactory constructor. * * @param SystemConfig $systemConfig */ public function __construct(SystemConfig $systemConfig) { $this->config = $systemConfig; if ($this->config->getValue('mysql.utf8mb4', false)) { $this->defaultConnectionParams['mysql']['charset'] = 'utf8mb4'; } } /** * @brief Get default connection parameters for a given DBMS. * @param string $type DBMS type * @throws \InvalidArgumentException If $type is invalid * @return array Default connection parameters. */ public function getDefaultConnectionParams($type) { $normalizedType = $this->normalizeType($type); if (!isset($this->defaultConnectionParams[$normalizedType])) { throw new \InvalidArgumentException("Unsupported type: $type"); } $result = $this->defaultConnectionParams[$normalizedType]; // \PDO::MYSQL_ATTR_FOUND_ROWS may not be defined, e.g. when the MySQL // driver is missing. In this case, we won't be able to connect anyway. if ($normalizedType === 'mysql' && defined('\PDO::MYSQL_ATTR_FOUND_ROWS')) { $result['driverOptions'] = array( \PDO::MYSQL_ATTR_FOUND_ROWS => true, ); } return $result; } /** * @brief Get default connection parameters for a given DBMS. * @param string $type DBMS type * @param array $additionalConnectionParams Additional connection parameters * @return \OC\DB\Connection */ public function getConnection($type, $additionalConnectionParams) { $normalizedType = $this->normalizeType($type); $eventManager = new EventManager(); switch ($normalizedType) { case 'mysql': $eventManager->addEventSubscriber( new SQLSessionInit("SET SESSION AUTOCOMMIT=1")); break; case 'oci': $eventManager->addEventSubscriber(new OracleSessionInit); // the driverOptions are unused in dbal and need to be mapped to the parameters if (isset($additionalConnectionParams['driverOptions'])) { $additionalConnectionParams = array_merge($additionalConnectionParams, $additionalConnectionParams['driverOptions']); } $host = $additionalConnectionParams['host']; $port = isset($additionalConnectionParams['port']) ? $additionalConnectionParams['port'] : null; $dbName = $additionalConnectionParams['dbname']; // we set the connect string as dbname and unset the host to coerce doctrine into using it as connect string if ($host === '') { $additionalConnectionParams['dbname'] = $dbName; // use dbname as easy connect name } else { $additionalConnectionParams['dbname'] = '//' . $host . (!empty($port) ? ":{$port}" : "") . '/' . $dbName; } unset($additionalConnectionParams['host']); break; case 'sqlite3': $journalMode = $additionalConnectionParams['sqlite.journal_mode']; $additionalConnectionParams['platform'] = new OCSqlitePlatform(); $eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode)); break; } /** @var Connection $connection */ $connection = DriverManager::getConnection( array_merge($this->getDefaultConnectionParams($type), $additionalConnectionParams), new Configuration(), $eventManager ); return $connection; } /** * @brief Normalize DBMS type * @param string $type DBMS type * @return string Normalized DBMS type */ public function normalizeType($type) { return $type === 'sqlite' ? 'sqlite3' : $type; } /** * Checks whether the specified DBMS type is valid. * * @param string $type * @return bool */ public function isValidType($type) { $normalizedType = $this->normalizeType($type); return isset($this->defaultConnectionParams[$normalizedType]); } /** * Create the connection parameters for the config * * @return array */ public function createConnectionParams() { $type = $this->config->getValue('dbtype', 'sqlite'); $connectionParams = [ 'user' => $this->config->getValue('dbuser', ''), 'password' => $this->config->getValue('dbpassword', ''), ]; $name = $this->config->getValue('dbname', 'owncloud'); if ($this->normalizeType($type) === 'sqlite3') { $dataDir = $this->config->getValue("datadirectory", \OC::$SERVERROOT . '/data'); $connectionParams['path'] = $dataDir . '/' . $name . '.db'; } else { $host = $this->config->getValue('dbhost', ''); if (strpos($host, ':')) { // Host variable may carry a port or socket. list($host, $portOrSocket) = explode(':', $host, 2); if (ctype_digit($portOrSocket)) { $connectionParams['port'] = $portOrSocket; } else { $connectionParams['unix_socket'] = $portOrSocket; } } $connectionParams['host'] = $host; $connectionParams['dbname'] = $name; } $connectionParams['tablePrefix'] = $this->config->getValue('dbtableprefix', 'oc_'); $connectionParams['sqlite.journal_mode'] = $this->config->getValue('sqlite.journal_mode', 'WAL'); //additional driver options, eg. for mysql ssl $driverOptions = $this->config->getValue('dbdriveroptions', null); if ($driverOptions) { $connectionParams['driverOptions'] = $driverOptions; } // set default table creation options $connectionParams['defaultTableOptions'] = [ 'collate' => 'utf8_bin', 'tablePrefix' => $connectionParams['tablePrefix'] ]; if ($this->config->getValue('mysql.utf8mb4', false)) { $connectionParams['defaultTableOptions'] = [ 'collate' => 'utf8mb4_bin', 'charset' => 'utf8mb4', 'row_format' => 'compressed', 'tablePrefix' => $connectionParams['tablePrefix'] ]; } return $connectionParams; } } private/DB/MDB2SchemaWriter.php 0000604 00000012560 15247130453 0012222 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author tbelau666 <thomas.belau@gmx.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\Index; class MDB2SchemaWriter { /** * @param string $file * @param \OC\DB\Connection $conn * @return bool */ static public function saveSchemaToFile($file, \OC\DB\Connection $conn) { $config = \OC::$server->getConfig(); $xml = new \SimpleXMLElement('<database/>'); $xml->addChild('name', $config->getSystemValue('dbname', 'owncloud')); $xml->addChild('create', 'true'); $xml->addChild('overwrite', 'false'); if($config->getSystemValue('dbtype', 'sqlite') === 'mysql' && $config->getSystemValue('mysql.utf8mb4', false)) { $xml->addChild('charset', 'utf8mb4'); } else { $xml->addChild('charset', 'utf8'); } // FIX ME: bloody work around if ($config->getSystemValue('dbtype', 'sqlite') === 'oci') { $filterExpression = '/^"' . preg_quote($conn->getPrefix()) . '/'; } else { $filterExpression = '/^' . preg_quote($conn->getPrefix()) . '/'; } $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); foreach ($conn->getSchemaManager()->listTables() as $table) { self::saveTable($table, $xml->addChild('table')); } file_put_contents($file, $xml->asXML()); return true; } /** * @param \Doctrine\DBAL\Schema\Table $table * @param \SimpleXMLElement $xml */ private static function saveTable($table, $xml) { $xml->addChild('name', $table->getName()); $declaration = $xml->addChild('declaration'); foreach($table->getColumns() as $column) { self::saveColumn($column, $declaration->addChild('field')); } foreach($table->getIndexes() as $index) { if ($index->getName() == 'PRIMARY') { $autoincrement = false; foreach($index->getColumns() as $column) { if ($table->getColumn($column)->getAutoincrement()) { $autoincrement = true; } } if ($autoincrement) { continue; } } self::saveIndex($index, $declaration->addChild('index')); } } /** * @param Column $column * @param \SimpleXMLElement $xml */ private static function saveColumn($column, $xml) { $xml->addChild('name', $column->getName()); switch($column->getType()) { case 'SmallInt': case 'Integer': case 'BigInt': $xml->addChild('type', 'integer'); $default = $column->getDefault(); if (is_null($default) && $column->getAutoincrement()) { $default = '0'; } $xml->addChild('default', $default); $xml->addChild('notnull', self::toBool($column->getNotnull())); if ($column->getAutoincrement()) { $xml->addChild('autoincrement', '1'); } if ($column->getUnsigned()) { $xml->addChild('unsigned', 'true'); } $length = '4'; if ($column->getType() == 'SmallInt') { $length = '2'; } elseif ($column->getType() == 'BigInt') { $length = '8'; } $xml->addChild('length', $length); break; case 'String': $xml->addChild('type', 'text'); $default = trim($column->getDefault()); if ($default === '') { $default = false; } $xml->addChild('default', $default); $xml->addChild('notnull', self::toBool($column->getNotnull())); $xml->addChild('length', $column->getLength()); break; case 'Text': $xml->addChild('type', 'clob'); $xml->addChild('notnull', self::toBool($column->getNotnull())); break; case 'Decimal': $xml->addChild('type', 'decimal'); $xml->addChild('default', $column->getDefault()); $xml->addChild('notnull', self::toBool($column->getNotnull())); $xml->addChild('length', '15'); break; case 'Boolean': $xml->addChild('type', 'integer'); $xml->addChild('default', $column->getDefault()); $xml->addChild('notnull', self::toBool($column->getNotnull())); $xml->addChild('length', '1'); break; case 'DateTime': $xml->addChild('type', 'timestamp'); $xml->addChild('default', $column->getDefault()); $xml->addChild('notnull', self::toBool($column->getNotnull())); break; } } /** * @param Index $index * @param \SimpleXMLElement $xml */ private static function saveIndex($index, $xml) { $xml->addChild('name', $index->getName()); if ($index->isPrimary()) { $xml->addChild('primary', 'true'); } elseif ($index->isUnique()) { $xml->addChild('unique', 'true'); } foreach($index->getColumns() as $column) { $field = $xml->addChild('field'); $field->addChild('name', $column); $field->addChild('sorting', 'ascending'); } } private static function toBool($bool) { return $bool ? 'true' : 'false'; } } private/DB/MySqlTools.php 0000604 00000002566 15247130453 0011313 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2017, ownCloud GmbH * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use OCP\IDBConnection; /** * Various MySQL specific helper functions. */ class MySqlTools { /** * @param Connection $connection * @return bool */ public function supports4ByteCharset(IDBConnection $connection) { foreach (['innodb_file_format' => 'Barracuda', 'innodb_large_prefix' => 'ON', 'innodb_file_per_table' => 'ON'] as $var => $val) { $result = $connection->executeQuery("SHOW VARIABLES LIKE '$var'"); $rows = $result->fetch(); $result->closeCursor(); if ($rows === false) { return false; } if (strcasecmp($rows['Value'], $val) !== 0) { return false; } } return true; } } private/DB/MySQLMigrator.php 0000604 00000005300 15247130453 0011664 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; class MySQLMigrator extends Migrator { /** * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { $platform = $connection->getDatabasePlatform(); $platform->registerDoctrineTypeMapping('enum', 'string'); $platform->registerDoctrineTypeMapping('bit', 'string'); $schemaDiff = parent::getDiff($targetSchema, $connection); // identifiers need to be quoted for mysql foreach ($schemaDiff->changedTables as $tableDiff) { $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); foreach ($tableDiff->changedColumns as $column) { $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); } } return $schemaDiff; } /** * Speed up migration test by disabling autocommit and unique indexes check * * @param \Doctrine\DBAL\Schema\Table $table * @throws \OC\DB\MigrationException */ protected function checkTableMigrate(Table $table) { $this->connection->exec('SET autocommit=0'); $this->connection->exec('SET unique_checks=0'); try { parent::checkTableMigrate($table); } catch (\Exception $e) { $this->connection->exec('SET unique_checks=1'); $this->connection->exec('SET autocommit=1'); throw new MigrationException($table->getName(), $e->getMessage()); } $this->connection->exec('SET unique_checks=1'); $this->connection->exec('SET autocommit=1'); } } private/DB/OCSqlitePlatform.php 0000604 00000002705 15247130453 0012410 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; class OCSqlitePlatform extends \Doctrine\DBAL\Platforms\SqlitePlatform { /** * {@inheritDoc} */ public function getColumnDeclarationSQL($name, array $field) { $def = parent::getColumnDeclarationSQL($name, $field); if (!empty($field['autoincrement'])) { $def .= ' PRIMARY KEY AUTOINCREMENT'; } return $def; } /** * {@inheritDoc} */ protected function _getCreateTableSQL($name, array $columns, array $options = array()){ // if auto increment is set the column is already defined as primary key foreach ($columns as $column) { if (!empty($column['autoincrement'])) { $options['primary'] = null; } } return parent::_getCreateTableSQL($name, $columns, $options); } } private/DB/SQLiteSessionInit.php 0000604 00000003714 15247130453 0012552 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Events; use Doctrine\Common\EventSubscriber; class SQLiteSessionInit implements EventSubscriber { /** * @var bool */ private $caseSensitiveLike; /** * @var string */ private $journalMode; /** * Configure case sensitive like for each connection * * @param bool $caseSensitiveLike * @param string $journalMode */ public function __construct($caseSensitiveLike, $journalMode) { $this->caseSensitiveLike = $caseSensitiveLike; $this->journalMode = $journalMode; } /** * @param ConnectionEventArgs $args * @return void */ public function postConnect(ConnectionEventArgs $args) { $sensitive = ($this->caseSensitiveLike) ? 'true' : 'false'; $args->getConnection()->executeUpdate('PRAGMA case_sensitive_like = ' . $sensitive); $args->getConnection()->executeUpdate('PRAGMA journal_mode = ' . $this->journalMode); /** @var \PDO $pdo */ $pdo = $args->getConnection()->getWrappedConnection(); $pdo->sqliteCreateFunction('md5', 'md5', 1); } public function getSubscribedEvents() { return array(Events::postConnect); } } private/DB/Connection.php 0000604 00000030716 15247130453 0011322 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\Common\EventManager; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Exception\ConstraintViolationException; use OC\DB\QueryBuilder\QueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\PreConditionNotMetException; class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { /** * @var string $tablePrefix */ protected $tablePrefix; /** * @var \OC\DB\Adapter $adapter */ protected $adapter; protected $lockedTable = null; public function connect() { try { return parent::connect(); } catch (DBALException $e) { // throw a new exception to prevent leaking info from the stacktrace throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode()); } } /** * Returns a QueryBuilder for the connection. * * @return \OCP\DB\QueryBuilder\IQueryBuilder */ public function getQueryBuilder() { return new QueryBuilder( $this, \OC::$server->getSystemConfig(), \OC::$server->getLogger() ); } /** * Gets the QueryBuilder for the connection. * * @return \Doctrine\DBAL\Query\QueryBuilder * @deprecated please use $this->getQueryBuilder() instead */ public function createQueryBuilder() { $backtrace = $this->getCallerBacktrace(); \OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]); return parent::createQueryBuilder(); } /** * Gets the ExpressionBuilder for the connection. * * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder * @deprecated please use $this->getQueryBuilder()->expr() instead */ public function getExpressionBuilder() { $backtrace = $this->getCallerBacktrace(); \OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]); return parent::getExpressionBuilder(); } /** * Get the file and line that called the method where `getCallerBacktrace()` was used * * @return string */ protected function getCallerBacktrace() { $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); // 0 is the method where we use `getCallerBacktrace` // 1 is the target method which uses the method we want to log if (isset($traces[1])) { return $traces[1]['file'] . ':' . $traces[1]['line']; } return ''; } /** * @return string */ public function getPrefix() { return $this->tablePrefix; } /** * Initializes a new instance of the Connection class. * * @param array $params The connection parameters. * @param \Doctrine\DBAL\Driver $driver * @param \Doctrine\DBAL\Configuration $config * @param \Doctrine\Common\EventManager $eventManager * @throws \Exception */ public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) { if (!isset($params['adapter'])) { throw new \Exception('adapter not set'); } if (!isset($params['tablePrefix'])) { throw new \Exception('tablePrefix not set'); } parent::__construct($params, $driver, $config, $eventManager); $this->adapter = new $params['adapter']($this); $this->tablePrefix = $params['tablePrefix']; parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED); } /** * Prepares an SQL statement. * * @param string $statement The SQL statement to prepare. * @param int $limit * @param int $offset * @return \Doctrine\DBAL\Driver\Statement The prepared statement. */ public function prepare( $statement, $limit=null, $offset=null ) { if ($limit === -1) { $limit = null; } if (!is_null($limit)) { $platform = $this->getDatabasePlatform(); $statement = $platform->modifyLimitQuery($statement, $limit, $offset); } $statement = $this->replaceTablePrefix($statement); $statement = $this->adapter->fixupStatement($statement); return parent::prepare($statement); } /** * Executes an, optionally parametrized, SQL query. * * If the query is parametrized, a prepared statement is used. * If an SQLLogger is configured, the execution is logged. * * @param string $query The SQL query to execute. * @param array $params The parameters to bind to the query, if any. * @param array $types The types the previous parameters are in. * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp The query cache profile, optional. * * @return \Doctrine\DBAL\Driver\Statement The executed statement. * * @throws \Doctrine\DBAL\DBALException */ public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) { $query = $this->replaceTablePrefix($query); $query = $this->adapter->fixupStatement($query); return parent::executeQuery($query, $params, $types, $qcp); } /** * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters * and returns the number of affected rows. * * This method supports PDO binding types as well as DBAL mapping types. * * @param string $query The SQL query. * @param array $params The query parameters. * @param array $types The parameter types. * * @return integer The number of affected rows. * * @throws \Doctrine\DBAL\DBALException */ public function executeUpdate($query, array $params = array(), array $types = array()) { $query = $this->replaceTablePrefix($query); $query = $this->adapter->fixupStatement($query); return parent::executeUpdate($query, $params, $types); } /** * Returns the ID of the last inserted row, or the last value from a sequence object, * depending on the underlying driver. * * Note: This method may not return a meaningful or consistent result across different drivers, * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY * columns or sequences. * * @param string $seqName Name of the sequence object from which the ID should be returned. * @return string A string representation of the last inserted ID. */ public function lastInsertId($seqName = null) { if ($seqName) { $seqName = $this->replaceTablePrefix($seqName); } return $this->adapter->lastInsertId($seqName); } // internal use public function realLastInsertId($seqName = null) { return parent::lastInsertId($seqName); } /** * Insert a row if the matching row does not exists. * * @param string $table The table name (will replace *PREFIX* with the actual prefix) * @param array $input data that should be inserted into the table (column name => value) * @param array|null $compare List of values that should be checked for "if not exists" * If this is null or an empty array, all keys of $input will be compared * Please note: text fields (clob) must not be used in the compare array * @return int number of inserted rows * @throws \Doctrine\DBAL\DBALException */ public function insertIfNotExist($table, $input, array $compare = null) { return $this->adapter->insertIfNotExist($table, $input, $compare); } private function getType($value) { if (is_bool($value)) { return IQueryBuilder::PARAM_BOOL; } else if (is_int($value)) { return IQueryBuilder::PARAM_INT; } else { return IQueryBuilder::PARAM_STR; } } /** * Insert or update a row value * * @param string $table * @param array $keys (column name => value) * @param array $values (column name => value) * @param array $updatePreconditionValues ensure values match preconditions (column name => value) * @return int number of new rows * @throws \Doctrine\DBAL\DBALException * @throws PreConditionNotMetException */ public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) { try { $insertQb = $this->getQueryBuilder(); $insertQb->insert($table) ->values( array_map(function($value) use ($insertQb) { return $insertQb->createNamedParameter($value, $this->getType($value)); }, array_merge($keys, $values)) ); return $insertQb->execute(); } catch (ConstraintViolationException $e) { // value already exists, try update $updateQb = $this->getQueryBuilder(); $updateQb->update($table); foreach ($values as $name => $value) { $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value))); } $where = $updateQb->expr()->andX(); $whereValues = array_merge($keys, $updatePreconditionValues); foreach ($whereValues as $name => $value) { $where->add($updateQb->expr()->eq( $name, $updateQb->createNamedParameter($value, $this->getType($value)), $this->getType($value) )); } $updateQb->where($where); $affected = $updateQb->execute(); if ($affected === 0 && !empty($updatePreconditionValues)) { throw new PreConditionNotMetException(); } return 0; } } /** * Create an exclusive read+write lock on a table * * @param string $tableName * @throws \BadMethodCallException When trying to acquire a second lock * @since 9.1.0 */ public function lockTable($tableName) { if ($this->lockedTable !== null) { throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.'); } $tableName = $this->tablePrefix . $tableName; $this->lockedTable = $tableName; $this->adapter->lockTable($tableName); } /** * Release a previous acquired lock again * * @since 9.1.0 */ public function unlockTable() { $this->adapter->unlockTable(); $this->lockedTable = null; } /** * returns the error code and message as a string for logging * works with DoctrineException * @return string */ public function getError() { $msg = $this->errorCode() . ': '; $errorInfo = $this->errorInfo(); if (is_array($errorInfo)) { $msg .= 'SQLSTATE = '.$errorInfo[0] . ', '; $msg .= 'Driver Code = '.$errorInfo[1] . ', '; $msg .= 'Driver Message = '.$errorInfo[2]; } return $msg; } /** * Drop a table from the database if it exists * * @param string $table table name without the prefix */ public function dropTable($table) { $table = $this->tablePrefix . trim($table); $schema = $this->getSchemaManager(); if($schema->tablesExist(array($table))) { $schema->dropTable($table); } } /** * Check if a table exists * * @param string $table table name without the prefix * @return bool */ public function tableExists($table){ $table = $this->tablePrefix . trim($table); $schema = $this->getSchemaManager(); return $schema->tablesExist(array($table)); } // internal use /** * @param string $statement * @return string */ protected function replaceTablePrefix($statement) { return str_replace( '*PREFIX*', $this->tablePrefix, $statement ); } /** * Check if a transaction is active * * @return bool * @since 8.2.0 */ public function inTransaction() { return $this->getTransactionNestingLevel() > 0; } /** * Espace a parameter to be used in a LIKE query * * @param string $param * @return string */ public function escapeLikeParameter($param) { return addcslashes($param, '\\_%'); } /** * Check whether or not the current database support 4byte wide unicode * * @return bool * @since 11.0.0 */ public function supports4ByteText() { if (!$this->getDatabasePlatform() instanceof MySqlPlatform) { return true; } return $this->getParams()['charset'] === 'utf8mb4'; } } private/DB/QueryBuilder/CompositeExpression.php 0000604 00000004427 15247130453 0015661 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder; use OCP\DB\QueryBuilder\ICompositeExpression; class CompositeExpression implements ICompositeExpression, \Countable { /** @var \Doctrine\DBAL\Query\Expression\CompositeExpression */ protected $compositeExpression; /** * Constructor. * * @param \Doctrine\DBAL\Query\Expression\CompositeExpression $compositeExpression */ public function __construct(\Doctrine\DBAL\Query\Expression\CompositeExpression $compositeExpression) { $this->compositeExpression = $compositeExpression; } /** * Adds multiple parts to composite expression. * * @param array $parts * * @return \OCP\DB\QueryBuilder\ICompositeExpression */ public function addMultiple(array $parts = array()) { $this->compositeExpression->addMultiple($parts); return $this; } /** * Adds an expression to composite expression. * * @param mixed $part * * @return \OCP\DB\QueryBuilder\ICompositeExpression */ public function add($part) { $this->compositeExpression->add($part); return $this; } /** * Retrieves the amount of expressions on composite expression. * * @return integer */ public function count() { return $this->compositeExpression->count(); } /** * Returns the type of this composite expression (AND/OR). * * @return string */ public function getType() { return $this->compositeExpression->getType(); } /** * Retrieves the string representation of this composite expression. * * @return string */ public function __toString() { return (string) $this->compositeExpression; } } private/DB/QueryBuilder/Parameter.php 0000604 00000002050 15247130453 0013545 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder; use OCP\DB\QueryBuilder\IParameter; class Parameter implements IParameter { /** @var mixed */ protected $name; public function __construct($name) { $this->name = $name; } /** * @return string */ public function __toString() { return (string) $this->name; } } private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php 0000604 00000002127 15247130453 0021204 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\DB\QueryBuilder\FunctionBuilder; use OC\DB\QueryBuilder\QueryFunction; class SqliteFunctionBuilder extends FunctionBuilder { public function concat($x, $y) { return new QueryFunction('(' . $this->helper->quoteColumnName($x) . ' || ' . $this->helper->quoteColumnName($y) . ')'); } } private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php 0000604 00000002150 15247130453 0020351 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\DB\QueryBuilder\FunctionBuilder; use OC\DB\QueryBuilder\QueryFunction; class OCIFunctionBuilder extends FunctionBuilder { public function md5($input) { return new QueryFunction('LOWER(DBMS_OBFUSCATION_TOOLKIT.md5 (input => UTL_RAW.cast_to_raw(' . $this->helper->quoteColumnName($input) .')))'); } } private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php 0000604 00000004004 15247130453 0020016 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\DB\QueryBuilder\FunctionBuilder; use OC\DB\QueryBuilder\QueryFunction; use OC\DB\QueryBuilder\QuoteHelper; use OCP\DB\QueryBuilder\IFunctionBuilder; class FunctionBuilder implements IFunctionBuilder { /** @var QuoteHelper */ protected $helper; /** * ExpressionBuilder constructor. * * @param QuoteHelper $helper */ public function __construct(QuoteHelper $helper) { $this->helper = $helper; } public function md5($input) { return new QueryFunction('MD5(' . $this->helper->quoteColumnName($input) . ')'); } public function concat($x, $y) { return new QueryFunction('CONCAT(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); } public function substring($input, $start, $length = null) { if ($length) { return new QueryFunction('SUBSTR(' . $this->helper->quoteColumnName($input) . ', ' . $this->helper->quoteColumnName($start) . ', ' . $this->helper->quoteColumnName($length) . ')'); } else { return new QueryFunction('SUBSTR(' . $this->helper->quoteColumnName($input) . ', ' . $this->helper->quoteColumnName($start) . ')'); } } public function sum($field) { return new QueryFunction('SUM(' . $this->helper->quoteColumnName($field) . ')'); } } private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php 0000604 00000002126 15247130453 0020770 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\DB\QueryBuilder\FunctionBuilder; use OC\DB\QueryBuilder\QueryFunction; class PgSqlFunctionBuilder extends FunctionBuilder { public function concat($x, $y) { return new QueryFunction('(' . $this->helper->quoteColumnName($x) . ' || ' . $this->helper->quoteColumnName($y) . ')'); } } private/DB/QueryBuilder/Literal.php 0000604 00000002060 15247130453 0013222 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder; use OCP\DB\QueryBuilder\ILiteral; class Literal implements ILiteral{ /** @var mixed */ protected $literal; public function __construct($literal) { $this->literal = $literal; } /** * @return string */ public function __toString() { return (string) $this->literal; } } private/DB/QueryBuilder/QueryBuilder.php 0000604 00000101305 15247130453 0014244 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; use OC\DB\OracleConnection; use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder; use OC\DB\QueryBuilder\ExpressionBuilder\MySqlExpressionBuilder; use OC\DB\QueryBuilder\ExpressionBuilder\OCIExpressionBuilder; use OC\DB\QueryBuilder\ExpressionBuilder\PgSqlExpressionBuilder; use OC\DB\QueryBuilder\ExpressionBuilder\SqliteExpressionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\FunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\OCIFunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\PgSqlFunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\SqliteFunctionBuilder; use OC\SystemConfig; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; use OCP\DB\QueryBuilder\IParameter; use OCP\IDBConnection; use OCP\ILogger; class QueryBuilder implements IQueryBuilder { /** @var \OCP\IDBConnection */ private $connection; /** @var SystemConfig */ private $systemConfig; /** @var ILogger */ private $logger; /** @var \Doctrine\DBAL\Query\QueryBuilder */ private $queryBuilder; /** @var QuoteHelper */ private $helper; /** @var bool */ private $automaticTablePrefix = true; /** @var string */ protected $lastInsertedTable; /** * Initializes a new QueryBuilder. * * @param IDBConnection $connection * @param SystemConfig $systemConfig * @param ILogger $logger */ public function __construct(IDBConnection $connection, SystemConfig $systemConfig, ILogger $logger) { $this->connection = $connection; $this->systemConfig = $systemConfig; $this->logger = $logger; $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection); $this->helper = new QuoteHelper(); } /** * Enable/disable automatic prefixing of table names with the oc_ prefix * * @param bool $enabled If set to true table names will be prefixed with the * owncloud database prefix automatically. * @since 8.2.0 */ public function automaticTablePrefix($enabled) { $this->automaticTablePrefix = (bool) $enabled; } /** * Gets an ExpressionBuilder used for object-oriented construction of query expressions. * This producer method is intended for convenient inline usage. Example: * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where($qb->expr()->eq('u.id', 1)); * </code> * * For more complex expression construction, consider storing the expression * builder object in a local variable. * * @return \OCP\DB\QueryBuilder\IExpressionBuilder */ public function expr() { if ($this->connection instanceof OracleConnection) { return new OCIExpressionBuilder($this->connection); } else if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { return new PgSqlExpressionBuilder($this->connection); } else if ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) { return new MySqlExpressionBuilder($this->connection); } else if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { return new SqliteExpressionBuilder($this->connection); } else { return new ExpressionBuilder($this->connection); } } /** * Gets an FunctionBuilder used for object-oriented construction of query functions. * This producer method is intended for convenient inline usage. Example: * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where($qb->fun()->md5('u.id')); * </code> * * For more complex function construction, consider storing the function * builder object in a local variable. * * @return \OCP\DB\QueryBuilder\IFunctionBuilder */ public function func() { if ($this->connection instanceof OracleConnection) { return new OCIFunctionBuilder($this->helper); } else if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { return new SqliteFunctionBuilder($this->helper); } else if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { return new PgSqlFunctionBuilder($this->helper); } else { return new FunctionBuilder($this->helper); } } /** * Gets the type of the currently built query. * * @return integer */ public function getType() { return $this->queryBuilder->getType(); } /** * Gets the associated DBAL Connection for this query builder. * * @return \OCP\IDBConnection */ public function getConnection() { return $this->connection; } /** * Gets the state of this query builder instance. * * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. */ public function getState() { return $this->queryBuilder->getState(); } /** * Executes this query using the bound parameters and their types. * * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} * for insert, update and delete statements. * * @return \Doctrine\DBAL\Driver\Statement|int */ public function execute() { if ($this->systemConfig->getValue('log_query', false)) { $params = []; foreach ($this->getParameters() as $placeholder => $value) { if (is_array($value)) { $params[] = $placeholder . ' => (\'' . implode('\', \'', $value) . '\')'; } else { $params[] = $placeholder . ' => \'' . $value . '\''; } } if (empty($params)) { $this->logger->debug('DB QueryBuilder: \'{query}\'', [ 'query' => $this->getSQL(), 'app' => 'core', ]); } else { $this->logger->debug('DB QueryBuilder: \'{query}\' with parameters: {params}', [ 'query' => $this->getSQL(), 'params' => implode(', ', $params), 'app' => 'core', ]); } } return $this->queryBuilder->execute(); } /** * Gets the complete SQL string formed by the current specifications of this QueryBuilder. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('User', 'u') * echo $qb->getSQL(); // SELECT u FROM User u * </code> * * @return string The SQL query string. */ public function getSQL() { return $this->queryBuilder->getSQL(); } /** * Sets a query parameter for the query being constructed. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.id = :user_id') * ->setParameter(':user_id', 1); * </code> * * @param string|integer $key The parameter position or name. * @param mixed $value The parameter value. * @param string|null $type One of the IQueryBuilder::PARAM_* constants. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function setParameter($key, $value, $type = null) { $this->queryBuilder->setParameter($key, $value, $type); return $this; } /** * Sets a collection of query parameters for the query being constructed. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.id = :user_id1 OR u.id = :user_id2') * ->setParameters(array( * ':user_id1' => 1, * ':user_id2' => 2 * )); * </code> * * @param array $params The query parameters to set. * @param array $types The query parameters types to set. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function setParameters(array $params, array $types = array()) { $this->queryBuilder->setParameters($params, $types); return $this; } /** * Gets all defined query parameters for the query being constructed indexed by parameter index or name. * * @return array The currently defined query parameters indexed by parameter index or name. */ public function getParameters() { return $this->queryBuilder->getParameters(); } /** * Gets a (previously set) query parameter of the query being constructed. * * @param mixed $key The key (index or name) of the bound parameter. * * @return mixed The value of the bound parameter. */ public function getParameter($key) { return $this->queryBuilder->getParameter($key); } /** * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. * * @return array The currently defined query parameter types indexed by parameter index or name. */ public function getParameterTypes() { return $this->queryBuilder->getParameterTypes(); } /** * Gets a (previously set) query parameter type of the query being constructed. * * @param mixed $key The key (index or name) of the bound parameter type. * * @return mixed The value of the bound parameter type. */ public function getParameterType($key) { return $this->queryBuilder->getParameterType($key); } /** * Sets the position of the first result to retrieve (the "offset"). * * @param integer $firstResult The first result to return. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function setFirstResult($firstResult) { $this->queryBuilder->setFirstResult($firstResult); return $this; } /** * Gets the position of the first result the query object was set to retrieve (the "offset"). * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. * * @return integer The position of the first result. */ public function getFirstResult() { return $this->queryBuilder->getFirstResult(); } /** * Sets the maximum number of results to retrieve (the "limit"). * * NOTE: Setting max results to "0" will cause mixed behaviour. While most * of the databases will just return an empty result set, Oracle will return * all entries. * * @param integer $maxResults The maximum number of results to retrieve. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function setMaxResults($maxResults) { $this->queryBuilder->setMaxResults($maxResults); return $this; } /** * Gets the maximum number of results the query object was set to retrieve (the "limit"). * Returns NULL if {@link setMaxResults} was not applied to this query builder. * * @return integer The maximum number of results. */ public function getMaxResults() { return $this->queryBuilder->getMaxResults(); } /** * Specifies an item that is to be returned in the query result. * Replaces any previously specified selections, if any. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.id', 'p.id') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); * </code> * * @param mixed $select The selection expressions. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function select($select = null) { $selects = is_array($select) ? $select : func_get_args(); $this->queryBuilder->select( $this->helper->quoteColumnNames($selects) ); return $this; } /** * Specifies an item that is to be returned with a different name in the query result. * * <code> * $qb = $conn->getQueryBuilder() * ->selectAlias('u.id', 'user_id') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); * </code> * * @param mixed $select The selection expressions. * @param string $alias The column alias used in the constructed query. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function selectAlias($select, $alias) { $this->queryBuilder->addSelect( $this->helper->quoteColumnName($select) . ' AS ' . $this->helper->quoteColumnName($alias) ); return $this; } /** * Specifies an item that is to be returned uniquely in the query result. * * <code> * $qb = $conn->getQueryBuilder() * ->selectDistinct('type') * ->from('users'); * </code> * * @param mixed $select The selection expressions. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function selectDistinct($select) { $this->queryBuilder->addSelect( 'DISTINCT ' . $this->helper->quoteColumnName($select) ); return $this; } /** * Adds an item that is to be returned in the query result. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.id') * ->addSelect('p.id') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); * </code> * * @param mixed $select The selection expression. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function addSelect($select = null) { $selects = is_array($select) ? $select : func_get_args(); $this->queryBuilder->addSelect( $this->helper->quoteColumnNames($selects) ); return $this; } /** * Turns the query being built into a bulk delete query that ranges over * a certain table. * * <code> * $qb = $conn->getQueryBuilder() * ->delete('users', 'u') * ->where('u.id = :user_id'); * ->setParameter(':user_id', 1); * </code> * * @param string $delete The table whose rows are subject to the deletion. * @param string $alias The table alias used in the constructed query. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function delete($delete = null, $alias = null) { $this->queryBuilder->delete( $this->getTableName($delete), $alias ); return $this; } /** * Turns the query being built into a bulk update query that ranges over * a certain table * * <code> * $qb = $conn->getQueryBuilder() * ->update('users', 'u') * ->set('u.password', md5('password')) * ->where('u.id = ?'); * </code> * * @param string $update The table whose rows are subject to the update. * @param string $alias The table alias used in the constructed query. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function update($update = null, $alias = null) { $this->queryBuilder->update( $this->getTableName($update), $alias ); return $this; } /** * Turns the query being built into an insert query that inserts into * a certain table * * <code> * $qb = $conn->getQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?', * 'password' => '?' * ) * ); * </code> * * @param string $insert The table into which the rows should be inserted. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function insert($insert = null) { $this->queryBuilder->insert( $this->getTableName($insert) ); $this->lastInsertedTable = $insert; return $this; } /** * Creates and adds a query root corresponding to the table identified by the * given alias, forming a cartesian product with any existing query roots. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.id') * ->from('users', 'u') * </code> * * @param string $from The table. * @param string|null $alias The alias of the table. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function from($from, $alias = null) { $this->queryBuilder->from( $this->getTableName($from), $this->quoteAlias($alias) ); return $this; } /** * Creates and adds a join to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * </code> * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function join($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->join( $this->quoteAlias($fromAlias), $this->getTableName($join), $this->quoteAlias($alias), $condition ); return $this; } /** * Creates and adds a join to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * </code> * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function innerJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->innerJoin( $this->quoteAlias($fromAlias), $this->getTableName($join), $this->quoteAlias($alias), $condition ); return $this; } /** * Creates and adds a left join to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * </code> * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function leftJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->leftJoin( $this->quoteAlias($fromAlias), $this->getTableName($join), $this->quoteAlias($alias), $condition ); return $this; } /** * Creates and adds a right join to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * </code> * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function rightJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->rightJoin( $this->quoteAlias($fromAlias), $this->getTableName($join), $this->quoteAlias($alias), $condition ); return $this; } /** * Sets a new value for a column in a bulk update query. * * <code> * $qb = $conn->getQueryBuilder() * ->update('users', 'u') * ->set('u.password', md5('password')) * ->where('u.id = ?'); * </code> * * @param string $key The column to set. * @param string $value The value, expression, placeholder, etc. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function set($key, $value) { $this->queryBuilder->set( $this->helper->quoteColumnName($key), $this->helper->quoteColumnName($value) ); return $this; } /** * Specifies one or more restrictions to the query result. * Replaces any previously specified restrictions, if any. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->where('u.id = ?'); * * // You can optionally programatically build and/or expressions * $qb = $conn->getQueryBuilder(); * * $or = $qb->expr()->orx(); * $or->add($qb->expr()->eq('u.id', 1)); * $or->add($qb->expr()->eq('u.id', 2)); * * $qb->update('users', 'u') * ->set('u.password', md5('password')) * ->where($or); * </code> * * @param mixed $predicates The restriction predicates. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function where($predicates) { call_user_func_array( [$this->queryBuilder, 'where'], func_get_args() ); return $this; } /** * Adds one or more restrictions to the query results, forming a logical * conjunction with any previously specified restrictions. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.username LIKE ?') * ->andWhere('u.is_active = 1'); * </code> * * @param mixed $where The query restrictions. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * * @see where() */ public function andWhere($where) { call_user_func_array( [$this->queryBuilder, 'andWhere'], func_get_args() ); return $this; } /** * Adds one or more restrictions to the query results, forming a logical * disjunction with any previously specified restrictions. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->where('u.id = 1') * ->orWhere('u.id = 2'); * </code> * * @param mixed $where The WHERE statement. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. * * @see where() */ public function orWhere($where) { call_user_func_array( [$this->queryBuilder, 'orWhere'], func_get_args() ); return $this; } /** * Specifies a grouping over the results of the query. * Replaces any previously specified groupings, if any. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->groupBy('u.id'); * </code> * * @param mixed $groupBy The grouping expression. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function groupBy($groupBy) { $groupBys = is_array($groupBy) ? $groupBy : func_get_args(); call_user_func_array( [$this->queryBuilder, 'groupBy'], $this->helper->quoteColumnNames($groupBys) ); return $this; } /** * Adds a grouping expression to the query. * * <code> * $qb = $conn->getQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->groupBy('u.lastLogin'); * ->addGroupBy('u.createdAt') * </code> * * @param mixed $groupBy The grouping expression. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function addGroupBy($groupBy) { $groupBys = is_array($groupBy) ? $groupBy : func_get_args(); call_user_func_array( [$this->queryBuilder, 'addGroupBy'], $this->helper->quoteColumnNames($groupBys) ); return $this; } /** * Sets a value for a column in an insert query. * * <code> * $qb = $conn->getQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?' * ) * ) * ->setValue('password', '?'); * </code> * * @param string $column The column into which the value should be inserted. * @param string $value The value that should be inserted into the column. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function setValue($column, $value) { $this->queryBuilder->setValue( $this->helper->quoteColumnName($column), $value ); return $this; } /** * Specifies values for an insert query indexed by column names. * Replaces any previous values, if any. * * <code> * $qb = $conn->getQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?', * 'password' => '?' * ) * ); * </code> * * @param array $values The values to specify for the insert query indexed by column names. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function values(array $values) { $quotedValues = []; foreach ($values as $key => $value) { $quotedValues[$this->helper->quoteColumnName($key)] = $value; } $this->queryBuilder->values($quotedValues); return $this; } /** * Specifies a restriction over the groups of the query. * Replaces any previous having restrictions, if any. * * @param mixed $having The restriction over the groups. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function having($having) { call_user_func_array( [$this->queryBuilder, 'having'], func_get_args() ); return $this; } /** * Adds a restriction over the groups of the query, forming a logical * conjunction with any existing having restrictions. * * @param mixed $having The restriction to append. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function andHaving($having) { call_user_func_array( [$this->queryBuilder, 'andHaving'], func_get_args() ); return $this; } /** * Adds a restriction over the groups of the query, forming a logical * disjunction with any existing having restrictions. * * @param mixed $having The restriction to add. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function orHaving($having) { call_user_func_array( [$this->queryBuilder, 'orHaving'], func_get_args() ); return $this; } /** * Specifies an ordering for the query results. * Replaces any previously specified orderings, if any. * * @param string $sort The ordering expression. * @param string $order The ordering direction. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function orderBy($sort, $order = null) { $this->queryBuilder->orderBy( $this->helper->quoteColumnName($sort), $order ); return $this; } /** * Adds an ordering to the query results. * * @param string $sort The ordering expression. * @param string $order The ordering direction. * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function addOrderBy($sort, $order = null) { $this->queryBuilder->addOrderBy( $this->helper->quoteColumnName($sort), $order ); return $this; } /** * Gets a query part by its name. * * @param string $queryPartName * * @return mixed */ public function getQueryPart($queryPartName) { return $this->queryBuilder->getQueryPart($queryPartName); } /** * Gets all query parts. * * @return array */ public function getQueryParts() { return $this->queryBuilder->getQueryParts(); } /** * Resets SQL parts. * * @param array|null $queryPartNames * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function resetQueryParts($queryPartNames = null) { $this->queryBuilder->resetQueryParts($queryPartNames); return $this; } /** * Resets a single SQL part. * * @param string $queryPartName * * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. */ public function resetQueryPart($queryPartName) { $this->queryBuilder->resetQueryPart($queryPartName); return $this; } /** * Creates a new named parameter and bind the value $value to it. * * This method provides a shortcut for PDOStatement::bindValue * when using prepared statements. * * The parameter $value specifies the value that you want to bind. If * $placeholder is not provided bindValue() will automatically create a * placeholder for you. An automatic placeholder will be of the name * ':dcValue1', ':dcValue2' etc. * * For more information see {@link http://php.net/pdostatement-bindparam} * * Example: * <code> * $value = 2; * $q->eq( 'id', $q->bindValue( $value ) ); * $stmt = $q->executeQuery(); // executed with 'id = 2' * </code> * * @license New BSD License * @link http://www.zetacomponents.org * * @param mixed $value * @param mixed $type * @param string $placeHolder The name to bind with. The string must start with a colon ':'. * * @return IParameter the placeholder name used. */ public function createNamedParameter($value, $type = IQueryBuilder::PARAM_STR, $placeHolder = null) { return new Parameter($this->queryBuilder->createNamedParameter($value, $type, $placeHolder)); } /** * Creates a new positional parameter and bind the given value to it. * * Attention: If you are using positional parameters with the query builder you have * to be very careful to bind all parameters in the order they appear in the SQL * statement , otherwise they get bound in the wrong order which can lead to serious * bugs in your code. * * Example: * <code> * $qb = $conn->getQueryBuilder(); * $qb->select('u.*') * ->from('users', 'u') * ->where('u.username = ' . $qb->createPositionalParameter('Foo', IQueryBuilder::PARAM_STR)) * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', IQueryBuilder::PARAM_STR)) * </code> * * @param mixed $value * @param integer $type * * @return IParameter */ public function createPositionalParameter($value, $type = IQueryBuilder::PARAM_STR) { return new Parameter($this->queryBuilder->createPositionalParameter($value, $type)); } /** * Creates a new parameter * * Example: * <code> * $qb = $conn->getQueryBuilder(); * $qb->select('u.*') * ->from('users', 'u') * ->where('u.username = ' . $qb->createParameter('name')) * ->setParameter('name', 'Bar', IQueryBuilder::PARAM_STR)) * </code> * * @param string $name * * @return IParameter */ public function createParameter($name) { return new Parameter(':' . $name); } /** * Creates a new function * * Attention: Column names inside the call have to be quoted before hand * * Example: * <code> * $qb = $conn->getQueryBuilder(); * $qb->select($qb->createFunction('COUNT(*)')) * ->from('users', 'u') * echo $qb->getSQL(); // SELECT COUNT(*) FROM `users` u * </code> * <code> * $qb = $conn->getQueryBuilder(); * $qb->select($qb->createFunction('COUNT(`column`)')) * ->from('users', 'u') * echo $qb->getSQL(); // SELECT COUNT(`column`) FROM `users` u * </code> * * @param string $call * * @return IQueryFunction */ public function createFunction($call) { return new QueryFunction($call); } /** * Used to get the id of the last inserted element * @return int * @throws \BadMethodCallException When being called before an insert query has been run. */ public function getLastInsertId() { if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::INSERT && $this->lastInsertedTable) { // lastInsertId() needs the prefix but no quotes $table = $this->prefixTableName($this->lastInsertedTable); return (int) $this->connection->lastInsertId($table); } throw new \BadMethodCallException('Invalid call to getLastInsertId without using insert() before.'); } /** * Returns the table name quoted and with database prefix as needed by the implementation * * @param string $table * @return string */ public function getTableName($table) { $table = $this->prefixTableName($table); return $this->helper->quoteColumnName($table); } /** * Returns the table name with database prefix as needed by the implementation * * @param string $table * @return string */ protected function prefixTableName($table) { if ($this->automaticTablePrefix === false || strpos($table, '*PREFIX*') === 0) { return $table; } return '*PREFIX*' . $table; } /** * Returns the column name quoted and with table alias prefix as needed by the implementation * * @param string $column * @param string $tableAlias * @return string */ public function getColumnName($column, $tableAlias = '') { if ($tableAlias !== '') { $tableAlias .= '.'; } return $this->helper->quoteColumnName($tableAlias . $column); } /** * Returns the column name quoted and with table alias prefix as needed by the implementation * * @param string $alias * @return string */ public function quoteAlias($alias) { if ($alias === '' || $alias === null) { return $alias; } return $this->helper->quoteColumnName($alias); } } private/DB/QueryBuilder/QuoteHelper.php 0000604 00000004330 15247130453 0014065 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder; use OCP\DB\QueryBuilder\ILiteral; use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryFunction; class QuoteHelper { /** * @param array|string|ILiteral|IParameter|IQueryFunction $strings string, Literal or Parameter * @return array|string */ public function quoteColumnNames($strings) { if (!is_array($strings)) { return $this->quoteColumnName($strings); } $return = []; foreach ($strings as $string) { $return[] = $this->quoteColumnName($string); } return $return; } /** * @param string|ILiteral|IParameter|IQueryFunction $string string, Literal or Parameter * @return string */ public function quoteColumnName($string) { if ($string instanceof IParameter || $string instanceof ILiteral || $string instanceof IQueryFunction) { return (string) $string; } if ($string === null || $string === 'null' || $string === '*') { return $string; } if (!is_string($string)) { throw new \InvalidArgumentException('Only strings, Literals and Parameters are allowed'); } $string = str_replace(' AS ', ' as ', $string); if (substr_count($string, ' as ')) { return implode(' as ', array_map([$this, 'quoteColumnName'], explode(' as ', $string, 2))); } if (substr_count($string, '.')) { list($alias, $columnName) = explode('.', $string, 2); if ($columnName === '*') { return '`' . $alias . '`.*'; } return '`' . $alias . '`.`' . $columnName . '`'; } return '`' . $string . '`'; } } private/DB/QueryBuilder/QueryFunction.php 0000604 00000002111 15247130453 0014436 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; class QueryFunction implements IQueryFunction { /** @var string */ protected $function; public function __construct($function) { $this->function = $function; } /** * @return string */ public function __toString() { return (string) $this->function; } } private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php 0000604 00000003016 15247130453 0021732 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder\ExpressionBuilder; use OC\DB\Connection; use OCP\IDBConnection; class MySqlExpressionBuilder extends ExpressionBuilder { /** @var string */ protected $charset; /** * @param \OCP\IDBConnection|Connection $connection */ public function __construct(IDBConnection $connection) { parent::__construct($connection); $params = $connection->getParams(); $this->charset = isset($params['charset']) ? $params['charset'] : 'utf8'; } /** * @inheritdoc */ public function iLike($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->charset . '_general_ci LIKE', $y); } } private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php 0000604 00000003176 15247130453 0021722 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder\ExpressionBuilder; use OC\DB\QueryBuilder\QueryFunction; use OCP\DB\QueryBuilder\IQueryBuilder; class PgSqlExpressionBuilder extends ExpressionBuilder { /** * Returns a IQueryFunction that casts the column to the given type * * @param string $column * @param mixed $type One of IQueryBuilder::PARAM_* * @return string */ public function castColumn($column, $type) { if ($type === IQueryBuilder::PARAM_INT) { $column = $this->helper->quoteColumnName($column); return new QueryFunction('CAST(' . $column . ' AS INT)'); } return parent::castColumn($column, $type); } /** * @inheritdoc */ public function iLike($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->comparison($x, 'ILIKE', $y); } } private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php 0000604 00000011416 15247130453 0021302 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder\ExpressionBuilder; use OC\DB\QueryBuilder\QueryFunction; use OCP\DB\QueryBuilder\ILiteral; use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; class OCIExpressionBuilder extends ExpressionBuilder { /** * @param mixed $column * @param mixed|null $type * @return array|IQueryFunction|string */ protected function prepareColumn($column, $type) { if ($type === IQueryBuilder::PARAM_STR && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) { $column = $this->castColumn($column, $type); } else { $column = $this->helper->quoteColumnNames($column); } return $column; } /** * @inheritdoc */ public function comparison($x, $operator, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->comparison($x, $operator, $y); } /** * @inheritdoc */ public function eq($x, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->eq($x, $y); } /** * @inheritdoc */ public function neq($x, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->neq($x, $y); } /** * @inheritdoc */ public function lt($x, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->lt($x, $y); } /** * @inheritdoc */ public function lte($x, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->lte($x, $y); } /** * @inheritdoc */ public function gt($x, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->gt($x, $y); } /** * @inheritdoc */ public function gte($x, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->gte($x, $y); } /** * @inheritdoc */ public function in($x, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->in($x, $y); } /** * @inheritdoc */ public function notIn($x, $y, $type = null) { $x = $this->prepareColumn($x, $type); $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->notIn($x, $y); } /** * Creates a $x = '' statement, because Oracle needs a different check * * @param string $x The field in string format to be inspected by the comparison. * @return string * @since 13.0.0 */ public function emptyString($x) { return $this->isNull($x); } /** * Creates a `$x <> ''` statement, because Oracle needs a different check * * @param string $x The field in string format to be inspected by the comparison. * @return string * @since 13.0.0 */ public function nonEmptyString($x) { return $this->isNotNull($x); } /** * Returns a IQueryFunction that casts the column to the given type * * @param string $column * @param mixed $type One of IQueryBuilder::PARAM_* * @return IQueryFunction */ public function castColumn($column, $type) { if ($type === IQueryBuilder::PARAM_STR) { $column = $this->helper->quoteColumnName($column); return new QueryFunction('to_char(' . $column . ')'); } return parent::castColumn($column, $type); } /** * @inheritdoc */ public function like($x, $y, $type = null) { return parent::like($x, $y, $type) . " ESCAPE '\\'"; } /** * @inheritdoc */ public function iLike($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return new QueryFunction('REGEXP_LIKE(' . $x . ', \'^\' || REPLACE(REPLACE(' . $y . ', \'%\', \'.*\'), \'_\', \'.\') || \'$\', \'i\')'); } } private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php 0000604 00000002032 15247130453 0022123 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\DB\QueryBuilder\ExpressionBuilder; class SqliteExpressionBuilder extends ExpressionBuilder { /** * @inheritdoc */ public function like($x, $y, $type = null) { return parent::like($x, $y, $type) . " ESCAPE '\\'"; } } private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php 0000604 00000033735 15247130453 0020757 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB\QueryBuilder\ExpressionBuilder; use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder; use OC\DB\QueryBuilder\CompositeExpression; use OC\DB\QueryBuilder\Literal; use OC\DB\QueryBuilder\QueryFunction; use OC\DB\QueryBuilder\QuoteHelper; use OCP\DB\QueryBuilder\IExpressionBuilder; use OCP\DB\QueryBuilder\ILiteral; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; use OCP\IDBConnection; class ExpressionBuilder implements IExpressionBuilder { /** @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder */ protected $expressionBuilder; /** @var QuoteHelper */ protected $helper; /** @var IDBConnection */ protected $connection; /** * Initializes a new <tt>ExpressionBuilder</tt>. * * @param \OCP\IDBConnection $connection */ public function __construct(IDBConnection $connection) { $this->connection = $connection; $this->helper = new QuoteHelper(); $this->expressionBuilder = new DoctrineExpressionBuilder($connection); } /** * Creates a conjunction of the given boolean expressions. * * Example: * * [php] * // (u.type = ?) AND (u.role = ?) * $expr->andX('u.type = ?', 'u.role = ?')); * * @param mixed $x Optional clause. Defaults = null, but requires * at least one defined when converting to string. * * @return \OCP\DB\QueryBuilder\ICompositeExpression */ public function andX($x = null) { $arguments = func_get_args(); $compositeExpression = call_user_func_array([$this->expressionBuilder, 'andX'], $arguments); return new CompositeExpression($compositeExpression); } /** * Creates a disjunction of the given boolean expressions. * * Example: * * [php] * // (u.type = ?) OR (u.role = ?) * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?')); * * @param mixed $x Optional clause. Defaults = null, but requires * at least one defined when converting to string. * * @return \OCP\DB\QueryBuilder\ICompositeExpression */ public function orX($x = null) { $arguments = func_get_args(); $compositeExpression = call_user_func_array([$this->expressionBuilder, 'orX'], $arguments); return new CompositeExpression($compositeExpression); } /** * Creates a comparison expression. * * @param mixed $x The left expression. * @param string $operator One of the IExpressionBuilder::* constants. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function comparison($x, $operator, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->comparison($x, $operator, $y); } /** * Creates an equality comparison expression with the given arguments. * * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> = <right expr>. Example: * * [php] * // u.id = ? * $expr->eq('u.id', '?'); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function eq($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->eq($x, $y); } /** * Creates a non equality comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> <> <right expr>. Example: * * [php] * // u.id <> 1 * $q->where($q->expr()->neq('u.id', '1')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function neq($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->neq($x, $y); } /** * Creates a lower-than comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> < <right expr>. Example: * * [php] * // u.id < ? * $q->where($q->expr()->lt('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function lt($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->lt($x, $y); } /** * Creates a lower-than-equal comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> <= <right expr>. Example: * * [php] * // u.id <= ? * $q->where($q->expr()->lte('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function lte($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->lte($x, $y); } /** * Creates a greater-than comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> > <right expr>. Example: * * [php] * // u.id > ? * $q->where($q->expr()->gt('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function gt($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->gt($x, $y); } /** * Creates a greater-than-equal comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <left expr> >= <right expr>. Example: * * [php] * // u.id >= ? * $q->where($q->expr()->gte('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function gte($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->gte($x, $y); } /** * Creates an IS NULL expression with the given arguments. * * @param string $x The field in string format to be restricted by IS NULL. * * @return string */ public function isNull($x) { $x = $this->helper->quoteColumnName($x); return $this->expressionBuilder->isNull($x); } /** * Creates an IS NOT NULL expression with the given arguments. * * @param string $x The field in string format to be restricted by IS NOT NULL. * * @return string */ public function isNotNull($x) { $x = $this->helper->quoteColumnName($x); return $this->expressionBuilder->isNotNull($x); } /** * Creates a LIKE() comparison expression with the given arguments. * * @param string $x Field in string format to be inspected by LIKE() comparison. * @param mixed $y Argument to be used in LIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function like($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->like($x, $y); } /** * Creates a ILIKE() comparison expression with the given arguments. * * @param string $x Field in string format to be inspected by ILIKE() comparison. * @param mixed $y Argument to be used in ILIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string * @since 9.0.0 */ public function iLike($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->comparison("LOWER($x)", 'LIKE', "LOWER($y)"); } /** * Creates a NOT LIKE() comparison expression with the given arguments. * * @param string $x Field in string format to be inspected by NOT LIKE() comparison. * @param mixed $y Argument to be used in NOT LIKE() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function notLike($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->notLike($x, $y); } /** * Creates a IN () comparison expression with the given arguments. * * @param string $x The field in string format to be inspected by IN() comparison. * @param string|array $y The placeholder or the array of values to be used by IN() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function in($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnNames($y); return $this->expressionBuilder->in($x, $y); } /** * Creates a NOT IN () comparison expression with the given arguments. * * @param string $x The field in string format to be inspected by NOT IN() comparison. * @param string|array $y The placeholder or the array of values to be used by NOT IN() comparison. * @param mixed|null $type one of the IQueryBuilder::PARAM_* constants * required when comparing text fields for oci compatibility * * @return string */ public function notIn($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnNames($y); return $this->expressionBuilder->notIn($x, $y); } /** * Creates a $x = '' statement, because Oracle needs a different check * * @param string $x The field in string format to be inspected by the comparison. * @return string * @since 13.0.0 */ public function emptyString($x) { return $this->eq($x, $this->literal('', IQueryBuilder::PARAM_STR)); } /** * Creates a `$x <> ''` statement, because Oracle needs a different check * * @param string $x The field in string format to be inspected by the comparison. * @return string * @since 13.0.0 */ public function nonEmptyString($x) { return $this->neq($x, $this->literal('', IQueryBuilder::PARAM_STR)); } /** * Binary AND Operator copies a bit to the result if it exists in both operands. * * @param string|ILiteral $x The field or value to check * @param int $y Bitmap that must be set * @return IQueryFunction * @since 12.0.0 */ public function bitwiseAnd($x, $y) { return new QueryFunction($this->connection->getDatabasePlatform()->getBitAndComparisonExpression( $this->helper->quoteColumnName($x), $y )); } /** * Binary OR Operator copies a bit if it exists in either operand. * * @param string|ILiteral $x The field or value to check * @param int $y Bitmap that must be set * @return IQueryFunction * @since 12.0.0 */ public function bitwiseOr($x, $y) { return new QueryFunction($this->connection->getDatabasePlatform()->getBitOrComparisonExpression( $this->helper->quoteColumnName($x), $y )); } /** * Quotes a given input parameter. * * @param mixed $input The parameter to be quoted. * @param mixed|null $type One of the IQueryBuilder::PARAM_* constants * * @return ILiteral */ public function literal($input, $type = null) { return new Literal($this->expressionBuilder->literal($input, $type)); } /** * Returns a IQueryFunction that casts the column to the given type * * @param string $column * @param mixed $type One of IQueryBuilder::PARAM_* * @return string */ public function castColumn($column, $type) { return new QueryFunction( $this->helper->quoteColumnName($column) ); } } private/DB/OracleConnection.php 0000604 00000005225 15247130453 0012445 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; class OracleConnection extends Connection { /** * Quote the keys of the array */ private function quoteKeys(array $data) { $return = array(); foreach($data as $key => $value) { $return[$this->quoteIdentifier($key)] = $value; } return $return; } /** * {@inheritDoc} */ public function insert($tableName, array $data, array $types = array()) { $tableName = $this->quoteIdentifier($tableName); $data = $this->quoteKeys($data); return parent::insert($tableName, $data, $types); } /** * {@inheritDoc} */ public function update($tableName, array $data, array $identifier, array $types = array()) { $tableName = $this->quoteIdentifier($tableName); $data = $this->quoteKeys($data); $identifier = $this->quoteKeys($identifier); return parent::update($tableName, $data, $identifier, $types); } /** * {@inheritDoc} */ public function delete($tableExpression, array $identifier, array $types = array()) { $tableName = $this->quoteIdentifier($tableExpression); $identifier = $this->quoteKeys($identifier); return parent::delete($tableName, $identifier); } /** * Drop a table from the database if it exists * * @param string $table table name without the prefix */ public function dropTable($table) { $table = $this->tablePrefix . trim($table); $table = $this->quoteIdentifier($table); $schema = $this->getSchemaManager(); if($schema->tablesExist(array($table))) { $schema->dropTable($table); } } /** * Check if a table exists * * @param string $table table name without the prefix * @return bool */ public function tableExists($table){ $table = $this->tablePrefix . trim($table); $table = $this->quoteIdentifier($table); $schema = $this->getSchemaManager(); return $schema->tablesExist(array($table)); } } private/DB/PostgreSqlMigrator.php 0000604 00000003500 15247130453 0013022 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\Schema\Schema; class PostgreSqlMigrator extends Migrator { /** * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { $schemaDiff = parent::getDiff($targetSchema, $connection); foreach ($schemaDiff->changedTables as $tableDiff) { // fix default value in brackets - pg 9.4 is returning a negative default value in () // see https://github.com/doctrine/dbal/issues/2427 foreach ($tableDiff->changedColumns as $column) { $column->changedProperties = array_filter($column->changedProperties, function ($changedProperties) use ($column) { if ($changedProperties !== 'default') { return true; } $fromDefault = $column->fromColumn->getDefault(); $toDefault = $column->column->getDefault(); $fromDefault = trim($fromDefault, "()"); // by intention usage of != return $fromDefault != $toDefault; }); } } return $schemaDiff; } } private/DB/MigrationException.php 0000604 00000002067 15247130453 0013031 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; class MigrationException extends \Exception { private $table; public function __construct($table, $message) { $this->table = $table; parent::__construct($message); } /** * @return string */ public function getTable() { return $this->table; } } private/DB/Adapter.php 0000604 00000006450 15247130453 0010601 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jonny007-MKD <1-23-4-5@web.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; /** * This handles the way we use to write queries, into something that can be * handled by the database abstraction layer. */ class Adapter { /** * @var \OC\DB\Connection $conn */ protected $conn; public function __construct($conn) { $this->conn = $conn; } /** * @param string $table name * @return int id of last insert statement */ public function lastInsertId($table) { return $this->conn->realLastInsertId($table); } /** * @param string $statement that needs to be changed so the db can handle it * @return string changed statement */ public function fixupStatement($statement) { return $statement; } /** * Create an exclusive read+write lock on a table * * @param string $tableName * @since 9.1.0 */ public function lockTable($tableName) { $this->conn->beginTransaction(); $this->conn->executeUpdate('LOCK TABLE `' .$tableName . '` IN EXCLUSIVE MODE'); } /** * Release a previous acquired lock again * * @since 9.1.0 */ public function unlockTable() { $this->conn->commit(); } /** * Insert a row if the matching row does not exists. * * @param string $table The table name (will replace *PREFIX* with the actual prefix) * @param array $input data that should be inserted into the table (column name => value) * @param array|null $compare List of values that should be checked for "if not exists" * If this is null or an empty array, all keys of $input will be compared * Please note: text fields (clob) must not be used in the compare array * @return int number of inserted rows * @throws \Doctrine\DBAL\DBALException */ public function insertIfNotExist($table, $input, array $compare = null) { if (empty($compare)) { $compare = array_keys($input); } $query = 'INSERT INTO `' .$table . '` (`' . implode('`,`', array_keys($input)) . '`) SELECT ' . str_repeat('?,', count($input)-1).'? ' // Is there a prettier alternative? . 'FROM `' . $table . '` WHERE '; $inserts = array_values($input); foreach($compare as $key) { $query .= '`' . $key . '`'; if (is_null($input[$key])) { $query .= ' IS NULL AND '; } else { $inserts[] = $input[$key]; $query .= ' = ? AND '; } } $query = substr($query, 0, strlen($query) - 5); $query .= ' HAVING COUNT(*) = 0'; return $this->conn->executeUpdate($query, $inserts); } } private/DB/MDB2SchemaManager.php 0000604 00000012714 15247130453 0012321 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\Schema; use OCP\IDBConnection; class MDB2SchemaManager { /** @var \OC\DB\Connection $conn */ protected $conn; /** * @param IDBConnection $conn */ public function __construct($conn) { $this->conn = $conn; } /** * saves database scheme to xml file * @param string $file name of file * @return bool * * TODO: write more documentation */ public function getDbStructure($file) { return \OC\DB\MDB2SchemaWriter::saveSchemaToFile($file, $this->conn); } /** * Creates tables from XML file * @param string $file file to read structure from * @return bool * * TODO: write more documentation */ public function createDbFromStructure($file) { $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $this->conn->getDatabasePlatform()); $toSchema = new Schema([], [], $this->conn->getSchemaManager()->createSchemaConfig()); $toSchema = $schemaReader->loadSchemaFromFile($file, $toSchema); return $this->executeSchemaChange($toSchema); } /** * @return \OC\DB\Migrator */ public function getMigrator() { $random = \OC::$server->getSecureRandom(); $platform = $this->conn->getDatabasePlatform(); $config = \OC::$server->getConfig(); $dispatcher = \OC::$server->getEventDispatcher(); if ($platform instanceof SqlitePlatform) { return new SQLiteMigrator($this->conn, $random, $config, $dispatcher); } else if ($platform instanceof OraclePlatform) { return new OracleMigrator($this->conn, $random, $config, $dispatcher); } else if ($platform instanceof MySqlPlatform) { return new MySQLMigrator($this->conn, $random, $config, $dispatcher); } else if ($platform instanceof PostgreSqlPlatform) { return new PostgreSqlMigrator($this->conn, $random, $config, $dispatcher); } else { return new NoCheckMigrator($this->conn, $random, $config, $dispatcher); } } /** * Reads database schema from file * * @param string $file file to read from * @return \Doctrine\DBAL\Schema\Schema */ private function readSchemaFromFile($file) { $platform = $this->conn->getDatabasePlatform(); $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $platform); $toSchema = new Schema([], [], $this->conn->getSchemaManager()->createSchemaConfig()); return $schemaReader->loadSchemaFromFile($file, $toSchema); } /** * update the database scheme * @param string $file file to read structure from * @param bool $generateSql only return the sql needed for the upgrade * @return string|boolean */ public function updateDbFromStructure($file, $generateSql = false) { $toSchema = $this->readSchemaFromFile($file); $migrator = $this->getMigrator(); if ($generateSql) { return $migrator->generateChangeScript($toSchema); } else { $migrator->migrate($toSchema); return true; } } /** * @param \Doctrine\DBAL\Schema\Schema $schema * @return string */ public function generateChangeScript($schema) { $migrator = $this->getMigrator(); return $migrator->generateChangeScript($schema); } /** * remove all tables defined in a database structure xml file * * @param string $file the xml file describing the tables */ public function removeDBStructure($file) { $schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $this->conn->getDatabasePlatform()); $toSchema = new Schema([], [], $this->conn->getSchemaManager()->createSchemaConfig()); $fromSchema = $schemaReader->loadSchemaFromFile($file, $toSchema); $toSchema = clone $fromSchema; /** @var $table \Doctrine\DBAL\Schema\Table */ foreach ($toSchema->getTables() as $table) { $toSchema->dropTable($table->getName()); } $comparator = new \Doctrine\DBAL\Schema\Comparator(); $schemaDiff = $comparator->compare($fromSchema, $toSchema); $this->executeSchemaChange($schemaDiff); } /** * @param \Doctrine\DBAL\Schema\Schema|\Doctrine\DBAL\Schema\SchemaDiff $schema * @return bool */ private function executeSchemaChange($schema) { $this->conn->beginTransaction(); foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { $this->conn->query($sql); } $this->conn->commit(); if ($this->conn->getDatabasePlatform() instanceof SqlitePlatform) { $this->conn->close(); $this->conn->connect(); } return true; } } private/DB/NoCheckMigrator.php 0000604 00000002177 15247130453 0012242 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\Schema\Schema; /** * migrator for database platforms that don't support the upgrade check * * @package OC\DB */ class NoCheckMigrator extends Migrator { /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema * @throws \OC\DB\MigrationException */ public function checkMigrate(Schema $targetSchema) {} } private/DB/Migrator.php 0000604 00000022423 15247130453 0011003 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author martin-rueegg <martin.rueegg@metaworx.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author tbelau666 <thomas.belau@gmx.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use \Doctrine\DBAL\DBALException; use \Doctrine\DBAL\Schema\Index; use \Doctrine\DBAL\Schema\Table; use \Doctrine\DBAL\Schema\Schema; use \Doctrine\DBAL\Schema\SchemaConfig; use \Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Types\StringType; use Doctrine\DBAL\Types\Type; use OCP\IConfig; use OCP\Security\ISecureRandom; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\GenericEvent; class Migrator { /** * @var \Doctrine\DBAL\Connection $connection */ protected $connection; /** * @var ISecureRandom */ private $random; /** @var IConfig */ protected $config; /** @var EventDispatcher */ private $dispatcher; /** @var bool */ private $noEmit = false; /** * @param \Doctrine\DBAL\Connection|Connection $connection * @param ISecureRandom $random * @param IConfig $config * @param EventDispatcher $dispatcher */ public function __construct(\Doctrine\DBAL\Connection $connection, ISecureRandom $random, IConfig $config, EventDispatcher $dispatcher = null) { $this->connection = $connection; $this->random = $random; $this->config = $config; $this->dispatcher = $dispatcher; } /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema */ public function migrate(Schema $targetSchema) { $this->noEmit = true; $this->applySchema($targetSchema); } /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema * @return string */ public function generateChangeScript(Schema $targetSchema) { $schemaDiff = $this->getDiff($targetSchema, $this->connection); $script = ''; $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); foreach ($sqls as $sql) { $script .= $this->convertStatementToScript($sql); } return $script; } /** * @param Schema $targetSchema * @throws \OC\DB\MigrationException */ public function checkMigrate(Schema $targetSchema) { $this->noEmit = true; /**@var \Doctrine\DBAL\Schema\Table[] $tables */ $tables = $targetSchema->getTables(); $filterExpression = $this->getFilterExpression(); $this->connection->getConfiguration()-> setFilterSchemaAssetsExpression($filterExpression); $existingTables = $this->connection->getSchemaManager()->listTableNames(); $step = 0; foreach ($tables as $table) { if (strpos($table->getName(), '.')) { list(, $tableName) = explode('.', $table->getName()); } else { $tableName = $table->getName(); } $this->emitCheckStep($tableName, $step++, count($tables)); // don't need to check for new tables if (array_search($tableName, $existingTables) !== false) { $this->checkTableMigrate($table); } } } /** * Create a unique name for the temporary table * * @param string $name * @return string */ protected function generateTemporaryTableName($name) { return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); } /** * Check the migration of a table on a copy so we can detect errors before messing with the real table * * @param \Doctrine\DBAL\Schema\Table $table * @throws \OC\DB\MigrationException */ protected function checkTableMigrate(Table $table) { $name = $table->getName(); $tmpName = $this->generateTemporaryTableName($name); $this->copyTable($name, $tmpName); //create the migration schema for the temporary table $tmpTable = $this->renameTableSchema($table, $tmpName); $schemaConfig = new SchemaConfig(); $schemaConfig->setName($this->connection->getDatabase()); $schema = new Schema(array($tmpTable), array(), $schemaConfig); try { $this->applySchema($schema); $this->dropTable($tmpName); } catch (DBALException $e) { // pgsql needs to commit it's failed transaction before doing anything else if ($this->connection->isTransactionActive()) { $this->connection->commit(); } $this->dropTable($tmpName); throw new MigrationException($table->getName(), $e->getMessage()); } } /** * @param \Doctrine\DBAL\Schema\Table $table * @param string $newName * @return \Doctrine\DBAL\Schema\Table */ protected function renameTableSchema(Table $table, $newName) { /** * @var \Doctrine\DBAL\Schema\Index[] $indexes */ $indexes = $table->getIndexes(); $newIndexes = array(); foreach ($indexes as $index) { if ($index->isPrimary()) { // do not rename primary key $indexName = $index->getName(); } else { // avoid conflicts in index names $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); } $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); } // foreign keys are not supported so we just set it to an empty array return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions()); } /** * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff * @throws DBALException */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { // adjust varchar columns with a length higher then getVarcharMaxLength to clob foreach ($targetSchema->getTables() as $table) { foreach ($table->getColumns() as $column) { if ($column->getType() instanceof StringType) { if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) { $column->setType(Type::getType('text')); $column->setLength(null); } } } } $filterExpression = $this->getFilterExpression(); $this->connection->getConfiguration()-> setFilterSchemaAssetsExpression($filterExpression); $sourceSchema = $connection->getSchemaManager()->createSchema(); // remove tables we don't know about /** @var $table \Doctrine\DBAL\Schema\Table */ foreach ($sourceSchema->getTables() as $table) { if (!$targetSchema->hasTable($table->getName())) { $sourceSchema->dropTable($table->getName()); } } // remove sequences we don't know about foreach ($sourceSchema->getSequences() as $table) { if (!$targetSchema->hasSequence($table->getName())) { $sourceSchema->dropSequence($table->getName()); } } $comparator = new Comparator(); return $comparator->compare($sourceSchema, $targetSchema); } /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection */ protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { if (is_null($connection)) { $connection = $this->connection; } $schemaDiff = $this->getDiff($targetSchema, $connection); $connection->beginTransaction(); $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); $step = 0; foreach ($sqls as $sql) { $this->emit($sql, $step++, count($sqls)); $connection->query($sql); } $connection->commit(); } /** * @param string $sourceName * @param string $targetName */ protected function copyTable($sourceName, $targetName) { $quotedSource = $this->connection->quoteIdentifier($sourceName); $quotedTarget = $this->connection->quoteIdentifier($targetName); $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); } /** * @param string $name */ protected function dropTable($name) { $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); } /** * @param $statement * @return string */ protected function convertStatementToScript($statement) { $script = $statement . ';'; $script .= PHP_EOL; $script .= PHP_EOL; return $script; } protected function getFilterExpression() { return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; } protected function emit($sql, $step, $max) { if ($this->noEmit) { return; } if(is_null($this->dispatcher)) { return; } $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max])); } private function emitCheckStep($tableName, $step, $max) { if(is_null($this->dispatcher)) { return; } $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max])); } } private/DB/SQLiteMigrator.php 0000604 00000005577 15247130453 0012100 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\DB; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Types\BigIntType; use Doctrine\DBAL\Types\Type; class SQLiteMigrator extends Migrator { /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema * @throws \OC\DB\MigrationException * * For sqlite we simple make a copy of the entire database, and test the migration on that */ public function checkMigrate(\Doctrine\DBAL\Schema\Schema $targetSchema) { $dbFile = $this->connection->getDatabase(); $tmpFile = $this->buildTempDatabase(); copy($dbFile, $tmpFile); $connectionParams = array( 'path' => $tmpFile, 'driver' => 'pdo_sqlite', ); $conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); try { $this->applySchema($targetSchema, $conn); $conn->close(); unlink($tmpFile); } catch (DBALException $e) { $conn->close(); unlink($tmpFile); throw new MigrationException('', $e->getMessage()); } } /** * @return string */ private function buildTempDatabase() { $dataDir = $this->config->getSystemValue("datadirectory", \OC::$SERVERROOT . '/data'); $tmpFile = uniqid("oc_"); return "$dataDir/$tmpFile.db"; } /** * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { $platform = $connection->getDatabasePlatform(); $platform->registerDoctrineTypeMapping('tinyint unsigned', 'integer'); $platform->registerDoctrineTypeMapping('smallint unsigned', 'integer'); $platform->registerDoctrineTypeMapping('varchar ', 'string'); // with sqlite autoincrement columns is of type integer foreach ($targetSchema->getTables() as $table) { foreach ($table->getColumns() as $column) { if ($column->getType() instanceof BigIntType && $column->getAutoincrement()) { $column->setType(Type::getType('integer')); } } } return parent::getDiff($targetSchema, $connection); } } private/Avatar.php 0000604 00000012112 15247130453 0010142 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\User\User; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IAvatar; use OCP\IConfig; use OCP\IImage; use OCP\IL10N; use OC_Image; use OCP\ILogger; /** * This class gets and sets users avatars. */ class Avatar implements IAvatar { /** @var ISimpleFolder */ private $folder; /** @var IL10N */ private $l; /** @var User */ private $user; /** @var ILogger */ private $logger; /** @var IConfig */ private $config; /** * constructor * * @param ISimpleFolder $folder The folder where the avatars are * @param IL10N $l * @param User $user * @param ILogger $logger * @param IConfig $config */ public function __construct(ISimpleFolder $folder, IL10N $l, $user, ILogger $logger, IConfig $config) { $this->folder = $folder; $this->l = $l; $this->user = $user; $this->logger = $logger; $this->config = $config; } /** * @inheritdoc */ public function get ($size = 64) { try { $file = $this->getFile($size); } catch (NotFoundException $e) { return false; } $avatar = new OC_Image(); $avatar->loadFromData($file->getContent()); return $avatar; } /** * Check if an avatar exists for the user * * @return bool */ public function exists() { return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png'); } /** * sets the users avatar * @param IImage|resource|string $data An image object, imagedata or path to set a new avatar * @throws \Exception if the provided file is not a jpg or png image * @throws \Exception if the provided image is not valid * @throws NotSquareException if the image is not square * @return void */ public function set ($data) { if($data instanceOf IImage) { $img = $data; $data = $img->data(); } else { $img = new OC_Image($data); } $type = substr($img->mimeType(), -3); if ($type === 'peg') { $type = 'jpg'; } if ($type !== 'jpg' && $type !== 'png') { throw new \Exception($this->l->t("Unknown filetype")); } if (!$img->valid()) { throw new \Exception($this->l->t("Invalid image")); } if (!($img->height() === $img->width())) { throw new NotSquareException($this->l->t("Avatar image is not square")); } $this->remove(); $this->folder->newFile('avatar.'.$type)->putContent($data); $this->user->triggerChange('avatar'); } /** * remove the users avatar * @return void */ public function remove () { $regex = '/^avatar\.([0-9]+\.)?(jpg|png)$/'; $avatars = $this->folder->getDirectoryListing(); $this->config->setUserValue($this->user->getUID(), 'avatar', 'version', (int)$this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1); foreach ($avatars as $avatar) { if (preg_match($regex, $avatar->getName())) { $avatar->delete(); } } $this->user->triggerChange('avatar'); } /** * @inheritdoc */ public function getFile($size) { $ext = $this->getExtension(); if ($size === -1) { $path = 'avatar.' . $ext; } else { $path = 'avatar.' . $size . '.' . $ext; } try { $file = $this->folder->getFile($path); } catch (NotFoundException $e) { if ($size <= 0) { throw new NotFoundException; } $avatar = new OC_Image(); /** @var ISimpleFile $file */ $file = $this->folder->getFile('avatar.' . $ext); $avatar->loadFromData($file->getContent()); if ($size !== -1) { $avatar->resize($size); } try { $file = $this->folder->newFile($path); $file->putContent($avatar->data()); } catch (NotPermittedException $e) { $this->logger->error('Failed to save avatar for ' . $this->user->getUID()); } } return $file; } /** * Get the extension of the avatar. If there is no avatar throw Exception * * @return string * @throws NotFoundException */ private function getExtension() { if ($this->folder->fileExists('avatar.jpg')) { return 'jpg'; } elseif ($this->folder->fileExists('avatar.png')) { return 'png'; } throw new NotFoundException; } } private/NeedsUpdateException.php 0000604 00000001560 15247130453 0013011 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class NeedsUpdateException extends ServiceUnavailableException { } private/Config.php 0000604 00000017216 15247130453 0010143 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Adam Williamson <awilliam@redhat.com> * @author Aldo "xoen" Giambelluca <xoen@xoen.org> * @author Bart Visscher <bartv@thisnet.nl> * @author Brice Maron <brice@bmaron.net> * @author Frank Karlitschek <frank@karlitschek.de> * @author Jakob Sack <mail@jakobsack.de> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; /** * This class is responsible for reading and writing config.php, the very basic * configuration file of ownCloud. */ class Config { const ENV_PREFIX = 'NC_'; /** @var array Associative array ($key => $value) */ protected $cache = array(); /** @var string */ protected $configDir; /** @var string */ protected $configFilePath; /** @var string */ protected $configFileName; /** * @param string $configDir Path to the config dir, needs to end with '/' * @param string $fileName (Optional) Name of the config file. Defaults to config.php */ public function __construct($configDir, $fileName = 'config.php') { $this->configDir = $configDir; $this->configFilePath = $this->configDir.$fileName; $this->configFileName = $fileName; $this->readData(); } /** * Lists all available config keys * * Please note that it does not return the values. * * @return array an array of key names */ public function getKeys() { return array_keys($this->cache); } /** * Returns a config value * * gets its value from an `NC_` prefixed environment variable * if it doesn't exist from config.php * if this doesn't exist either, it will return the given `$default` * * @param string $key key * @param mixed $default = null default value * @return mixed the value or $default */ public function getValue($key, $default = null) { $envValue = getenv(self::ENV_PREFIX . $key); if ($envValue !== false) { return $envValue; } if (isset($this->cache[$key])) { return $this->cache[$key]; } return $default; } /** * Sets and deletes values and writes the config.php * * @param array $configs Associative array with `key => value` pairs * If value is null, the config key will be deleted */ public function setValues(array $configs) { $needsUpdate = false; foreach ($configs as $key => $value) { if ($value !== null) { $needsUpdate |= $this->set($key, $value); } else { $needsUpdate |= $this->delete($key); } } if ($needsUpdate) { // Write changes $this->writeData(); } } /** * Sets the value and writes it to config.php if required * * @param string $key key * @param mixed $value value */ public function setValue($key, $value) { if ($this->set($key, $value)) { // Write changes $this->writeData(); } } /** * This function sets the value * * @param string $key key * @param mixed $value value * @return bool True if the file needs to be updated, false otherwise */ protected function set($key, $value) { if (!isset($this->cache[$key]) || $this->cache[$key] !== $value) { // Add change $this->cache[$key] = $value; return true; } return false; } /** * Removes a key from the config and removes it from config.php if required * @param string $key */ public function deleteKey($key) { if ($this->delete($key)) { // Write changes $this->writeData(); } } /** * This function removes a key from the config * * @param string $key * @return bool True if the file needs to be updated, false otherwise */ protected function delete($key) { if (isset($this->cache[$key])) { // Delete key from cache unset($this->cache[$key]); return true; } return false; } /** * Loads the config file * * Reads the config file and saves it to the cache * * @throws \Exception If no lock could be acquired or the config file has not been found */ private function readData() { // Default config should always get loaded $configFiles = array($this->configFilePath); // Add all files in the config dir ending with the same file name $extra = glob($this->configDir.'*.'.$this->configFileName); if (is_array($extra)) { natsort($extra); $configFiles = array_merge($configFiles, $extra); } // Include file and merge config foreach ($configFiles as $file) { $fileExistsAndIsReadable = file_exists($file) && is_readable($file); $filePointer = $fileExistsAndIsReadable ? fopen($file, 'r') : false; if($file === $this->configFilePath && $filePointer === false) { // Opening the main config might not be possible, e.g. if the wrong // permissions are set (likely on a new installation) continue; } // Try to acquire a file lock if(!flock($filePointer, LOCK_SH)) { throw new \Exception(sprintf('Could not acquire a shared lock on the config file %s', $file)); } unset($CONFIG); include $file; if(isset($CONFIG) && is_array($CONFIG)) { $this->cache = array_merge($this->cache, $CONFIG); } // Close the file pointer and release the lock flock($filePointer, LOCK_UN); fclose($filePointer); } } /** * Writes the config file * * Saves the config to the config file. * * @throws HintException If the config file cannot be written to * @throws \Exception If no file lock can be acquired */ private function writeData() { // Create a php file ... $content = "<?php\n"; $content .= '$CONFIG = '; $content .= var_export($this->cache, true); $content .= ";\n"; touch ($this->configFilePath); $filePointer = fopen($this->configFilePath, 'r+'); // Prevent others not to read the config chmod($this->configFilePath, 0640); // File does not exist, this can happen when doing a fresh install if(!is_resource ($filePointer)) { // TODO fix this via DI once it is very clear that this doesn't cause side effects due to initialization order // currently this breaks app routes but also could have other side effects especially during setup and exception handling $url = \OC::$server->getURLGenerator()->linkToDocs('admin-dir_permissions'); throw new HintException( "Can't write into config directory!", 'This can usually be fixed by giving the webserver write access to the config directory. See ' . $url); } // Try to acquire a file lock if(!flock($filePointer, LOCK_EX)) { throw new \Exception(sprintf('Could not acquire an exclusive lock on the config file %s', $this->configFilePath)); } // Write the config and release the lock ftruncate ($filePointer, 0); fwrite($filePointer, $content); fflush($filePointer); flock($filePointer, LOCK_UN); fclose($filePointer); // Try invalidating the opcache just for the file we wrote... if (!\OC_Util::deleteFromOpcodeCache($this->configFilePath)) { // But if that doesn't work, clear the whole cache. \OC_Util::clearOpcodeCache(); } } } private/IntegrityCheck/Checker.php 0000604 00000044130 15247130453 0013211 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\IntegrityCheck; use OC\IntegrityCheck\Exceptions\InvalidSignatureException; use OC\IntegrityCheck\Helpers\AppLocator; use OC\IntegrityCheck\Helpers\EnvironmentHelper; use OC\IntegrityCheck\Helpers\FileAccessHelper; use OC\IntegrityCheck\Iterator\ExcludeFileByNameFilterIterator; use OC\IntegrityCheck\Iterator\ExcludeFoldersByPathFilterIterator; use OCP\App\IAppManager; use OCP\ICache; use OCP\ICacheFactory; use OCP\IConfig; use OCP\ITempManager; use phpseclib\Crypt\RSA; use phpseclib\File\X509; /** * Class Checker handles the code signing using X.509 and RSA. ownCloud ships with * a public root certificate certificate that allows to issue new certificates that * will be trusted for signing code. The CN will be used to verify that a certificate * given to a third-party developer may not be used for other applications. For * example the author of the application "calendar" would only receive a certificate * only valid for this application. * * @package OC\IntegrityCheck */ class Checker { const CACHE_KEY = 'oc.integritycheck.checker'; /** @var EnvironmentHelper */ private $environmentHelper; /** @var AppLocator */ private $appLocator; /** @var FileAccessHelper */ private $fileAccessHelper; /** @var IConfig */ private $config; /** @var ICache */ private $cache; /** @var IAppManager */ private $appManager; /** @var ITempManager */ private $tempManager; /** * @param EnvironmentHelper $environmentHelper * @param FileAccessHelper $fileAccessHelper * @param AppLocator $appLocator * @param IConfig $config * @param ICacheFactory $cacheFactory * @param IAppManager $appManager * @param ITempManager $tempManager */ public function __construct(EnvironmentHelper $environmentHelper, FileAccessHelper $fileAccessHelper, AppLocator $appLocator, IConfig $config = null, ICacheFactory $cacheFactory, IAppManager $appManager = null, ITempManager $tempManager) { $this->environmentHelper = $environmentHelper; $this->fileAccessHelper = $fileAccessHelper; $this->appLocator = $appLocator; $this->config = $config; $this->cache = $cacheFactory->create(self::CACHE_KEY); $this->appManager = $appManager; $this->tempManager = $tempManager; } /** * Whether code signing is enforced or not. * * @return bool */ public function isCodeCheckEnforced() { $notSignedChannels = [ '', 'git']; if (in_array($this->environmentHelper->getChannel(), $notSignedChannels, true)) { return false; } /** * This config option is undocumented and supposed to be so, it's only * applicable for very specific scenarios and we should not advertise it * too prominent. So please do not add it to config.sample.php. */ if ($this->config !== null) { $isIntegrityCheckDisabled = $this->config->getSystemValue('integrity.check.disabled', false); } else { $isIntegrityCheckDisabled = false; } if ($isIntegrityCheckDisabled === true) { return false; } return true; } /** * Enumerates all files belonging to the folder. Sensible defaults are excluded. * * @param string $folderToIterate * @param string $root * @return \RecursiveIteratorIterator * @throws \Exception */ private function getFolderIterator($folderToIterate, $root = '') { $dirItr = new \RecursiveDirectoryIterator( $folderToIterate, \RecursiveDirectoryIterator::SKIP_DOTS ); if($root === '') { $root = \OC::$SERVERROOT; } $root = rtrim($root, '/'); $excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr); $excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root); return new \RecursiveIteratorIterator( $excludeFoldersIterator, \RecursiveIteratorIterator::SELF_FIRST ); } /** * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found * in the iterator. * * @param \RecursiveIteratorIterator $iterator * @param string $path * @return array Array of hashes. */ private function generateHashes(\RecursiveIteratorIterator $iterator, $path) { $hashes = []; $copiedWebserverSettingFiles = false; $tmpFolder = ''; $baseDirectoryLength = strlen($path); foreach($iterator as $filename => $data) { /** @var \DirectoryIterator $data */ if($data->isDir()) { continue; } $relativeFileName = substr($filename, $baseDirectoryLength); $relativeFileName = ltrim($relativeFileName, '/'); // Exclude signature.json files in the appinfo and root folder if($relativeFileName === 'appinfo/signature.json') { continue; } // Exclude signature.json files in the appinfo and core folder if($relativeFileName === 'core/signature.json') { continue; } // The .user.ini and the .htaccess file of ownCloud can contain some // custom modifications such as for example the maximum upload size // to ensure that this will not lead to false positives this will // copy the file to a temporary folder and reset it to the default // values. if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess' || $filename === $this->environmentHelper->getServerRoot() . '/.user.ini') { if(!$copiedWebserverSettingFiles) { $tmpFolder = rtrim($this->tempManager->getTemporaryFolder(), '/'); copy($this->environmentHelper->getServerRoot() . '/.htaccess', $tmpFolder . '/.htaccess'); copy($this->environmentHelper->getServerRoot() . '/.user.ini', $tmpFolder . '/.user.ini'); \OC_Files::setUploadLimit( \OCP\Util::computerFileSize('511MB'), [ '.htaccess' => $tmpFolder . '/.htaccess', '.user.ini' => $tmpFolder . '/.user.ini', ] ); } } // The .user.ini file can contain custom modifications to the file size // as well. if($filename === $this->environmentHelper->getServerRoot() . '/.user.ini') { $fileContent = file_get_contents($tmpFolder . '/.user.ini'); $hashes[$relativeFileName] = hash('sha512', $fileContent); continue; } // The .htaccess file in the root folder of ownCloud can contain // custom content after the installation due to the fact that dynamic // content is written into it at installation time as well. This // includes for example the 404 and 403 instructions. // Thus we ignore everything below the first occurrence of // "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the // hash generated based on this. if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') { $fileContent = file_get_contents($tmpFolder . '/.htaccess'); $explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent); if(count($explodedArray) === 2) { $hashes[$relativeFileName] = hash('sha512', $explodedArray[0]); continue; } } $hashes[$relativeFileName] = hash_file('sha512', $filename); } return $hashes; } /** * Creates the signature data * * @param array $hashes * @param X509 $certificate * @param RSA $privateKey * @return string */ private function createSignatureData(array $hashes, X509 $certificate, RSA $privateKey) { ksort($hashes); $privateKey->setSignatureMode(RSA::SIGNATURE_PSS); $privateKey->setMGFHash('sha512'); // See https://tools.ietf.org/html/rfc3447#page-38 $privateKey->setSaltLength(0); $signature = $privateKey->sign(json_encode($hashes)); return [ 'hashes' => $hashes, 'signature' => base64_encode($signature), 'certificate' => $certificate->saveX509($certificate->currentCert), ]; } /** * Write the signature of the app in the specified folder * * @param string $path * @param X509 $certificate * @param RSA $privateKey * @throws \Exception */ public function writeAppSignature($path, X509 $certificate, RSA $privateKey) { $appInfoDir = $path . '/appinfo'; try { $this->fileAccessHelper->assertDirectoryExists($appInfoDir); $iterator = $this->getFolderIterator($path); $hashes = $this->generateHashes($iterator, $path); $signature = $this->createSignatureData($hashes, $certificate, $privateKey); $this->fileAccessHelper->file_put_contents( $appInfoDir . '/signature.json', json_encode($signature, JSON_PRETTY_PRINT) ); } catch (\Exception $e){ if (!$this->fileAccessHelper->is_writable($appInfoDir)) { throw new \Exception($appInfoDir . ' is not writable'); } throw $e; } } /** * Write the signature of core * * @param X509 $certificate * @param RSA $rsa * @param string $path * @throws \Exception */ public function writeCoreSignature(X509 $certificate, RSA $rsa, $path) { $coreDir = $path . '/core'; try { $this->fileAccessHelper->assertDirectoryExists($coreDir); $iterator = $this->getFolderIterator($path, $path); $hashes = $this->generateHashes($iterator, $path); $signatureData = $this->createSignatureData($hashes, $certificate, $rsa); $this->fileAccessHelper->file_put_contents( $coreDir . '/signature.json', json_encode($signatureData, JSON_PRETTY_PRINT) ); } catch (\Exception $e){ if (!$this->fileAccessHelper->is_writable($coreDir)) { throw new \Exception($coreDir . ' is not writable'); } throw $e; } } /** * Verifies the signature for the specified path. * * @param string $signaturePath * @param string $basePath * @param string $certificateCN * @return array * @throws InvalidSignatureException * @throws \Exception */ private function verify($signaturePath, $basePath, $certificateCN) { if(!$this->isCodeCheckEnforced()) { return []; } $signatureData = json_decode($this->fileAccessHelper->file_get_contents($signaturePath), true); if(!is_array($signatureData)) { throw new InvalidSignatureException('Signature data not found.'); } $expectedHashes = $signatureData['hashes']; ksort($expectedHashes); $signature = base64_decode($signatureData['signature']); $certificate = $signatureData['certificate']; // Check if certificate is signed by Nextcloud Root Authority $x509 = new \phpseclib\File\X509(); $rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt'); $x509->loadCA($rootCertificatePublicKey); $x509->loadX509($certificate); if(!$x509->validateSignature()) { throw new InvalidSignatureException('Certificate is not valid.'); } // Verify if certificate has proper CN. "core" CN is always trusted. if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') { throw new InvalidSignatureException( sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN']) ); } // Check if the signature of the files is valid $rsa = new \phpseclib\Crypt\RSA(); $rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']); $rsa->setSignatureMode(RSA::SIGNATURE_PSS); $rsa->setMGFHash('sha512'); // See https://tools.ietf.org/html/rfc3447#page-38 $rsa->setSaltLength(0); if(!$rsa->verify(json_encode($expectedHashes), $signature)) { throw new InvalidSignatureException('Signature could not get verified.'); } // Fixes for the updater as shipped with ownCloud 9.0.x: The updater is // replaced after the code integrity check is performed. // // Due to this reason we exclude the whole updater/ folder from the code // integrity check. if($basePath === $this->environmentHelper->getServerRoot()) { foreach($expectedHashes as $fileName => $hash) { if(strpos($fileName, 'updater/') === 0) { unset($expectedHashes[$fileName]); } } } // Compare the list of files which are not identical $currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath); $differencesA = array_diff($expectedHashes, $currentInstanceHashes); $differencesB = array_diff($currentInstanceHashes, $expectedHashes); $differences = array_unique(array_merge($differencesA, $differencesB)); $differenceArray = []; foreach($differences as $filename => $hash) { // Check if file should not exist in the new signature table if(!array_key_exists($filename, $expectedHashes)) { $differenceArray['EXTRA_FILE'][$filename]['expected'] = ''; $differenceArray['EXTRA_FILE'][$filename]['current'] = $hash; continue; } // Check if file is missing if(!array_key_exists($filename, $currentInstanceHashes)) { $differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename]; $differenceArray['FILE_MISSING'][$filename]['current'] = ''; continue; } // Check if hash does mismatch if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) { $differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename]; $differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename]; continue; } // Should never happen. throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.'); } return $differenceArray; } /** * Whether the code integrity check has passed successful or not * * @return bool */ public function hasPassedCheck() { $results = $this->getResults(); if(empty($results)) { return true; } return false; } /** * @return array */ public function getResults() { $cachedResults = $this->cache->get(self::CACHE_KEY); if(!is_null($cachedResults)) { return json_decode($cachedResults, true); } if ($this->config !== null) { return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true); } return []; } /** * Stores the results in the app config as well as cache * * @param string $scope * @param array $result */ private function storeResults($scope, array $result) { $resultArray = $this->getResults(); unset($resultArray[$scope]); if(!empty($result)) { $resultArray[$scope] = $result; } if ($this->config !== null) { $this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray)); } $this->cache->set(self::CACHE_KEY, json_encode($resultArray)); } /** * * Clean previous results for a proper rescanning. Otherwise */ private function cleanResults() { $this->config->deleteAppValue('core', self::CACHE_KEY); $this->cache->remove(self::CACHE_KEY); } /** * Verify the signature of $appId. Returns an array with the following content: * [ * 'FILE_MISSING' => * [ * 'filename' => [ * 'expected' => 'expectedSHA512', * 'current' => 'currentSHA512', * ], * ], * 'EXTRA_FILE' => * [ * 'filename' => [ * 'expected' => 'expectedSHA512', * 'current' => 'currentSHA512', * ], * ], * 'INVALID_HASH' => * [ * 'filename' => [ * 'expected' => 'expectedSHA512', * 'current' => 'currentSHA512', * ], * ], * ] * * Array may be empty in case no problems have been found. * * @param string $appId * @param string $path Optional path. If none is given it will be guessed. * @return array */ public function verifyAppSignature($appId, $path = '') { try { if($path === '') { $path = $this->appLocator->getAppPath($appId); } $result = $this->verify( $path . '/appinfo/signature.json', $path, $appId ); } catch (\Exception $e) { $result = [ 'EXCEPTION' => [ 'class' => get_class($e), 'message' => $e->getMessage(), ], ]; } $this->storeResults($appId, $result); return $result; } /** * Verify the signature of core. Returns an array with the following content: * [ * 'FILE_MISSING' => * [ * 'filename' => [ * 'expected' => 'expectedSHA512', * 'current' => 'currentSHA512', * ], * ], * 'EXTRA_FILE' => * [ * 'filename' => [ * 'expected' => 'expectedSHA512', * 'current' => 'currentSHA512', * ], * ], * 'INVALID_HASH' => * [ * 'filename' => [ * 'expected' => 'expectedSHA512', * 'current' => 'currentSHA512', * ], * ], * ] * * Array may be empty in case no problems have been found. * * @return array */ public function verifyCoreSignature() { try { $result = $this->verify( $this->environmentHelper->getServerRoot() . '/core/signature.json', $this->environmentHelper->getServerRoot(), 'core' ); } catch (\Exception $e) { $result = [ 'EXCEPTION' => [ 'class' => get_class($e), 'message' => $e->getMessage(), ], ]; } $this->storeResults('core', $result); return $result; } /** * Verify the core code of the instance as well as all applicable applications * and store the results. */ public function runInstanceVerification() { $this->cleanResults(); $this->verifyCoreSignature(); $appIds = $this->appLocator->getAllApps(); foreach($appIds as $appId) { // If an application is shipped a valid signature is required $isShipped = $this->appManager->isShipped($appId); $appNeedsToBeChecked = false; if ($isShipped) { $appNeedsToBeChecked = true; } elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) { // Otherwise only if the application explicitly ships a signature.json file $appNeedsToBeChecked = true; } if($appNeedsToBeChecked) { $this->verifyAppSignature($appId); } } } } private/IntegrityCheck/Exceptions/InvalidSignatureException.php 0000604 00000002031 15247130453 0021107 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\IntegrityCheck\Exceptions; /** * Class InvalidSignatureException is thrown in case the signature of the hashes * cannot be properly validated. This indicates that either files * * @package OC\IntegrityCheck\Exceptions */ class InvalidSignatureException extends \Exception {} private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php 0000604 00000004053 15247130453 0022336 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author RealRancor <Fisch.666@gmx.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\IntegrityCheck\Iterator; class ExcludeFoldersByPathFilterIterator extends \RecursiveFilterIterator { private $excludedFolders = []; public function __construct(\RecursiveIterator $iterator, $root = '') { parent::__construct($iterator); $appFolders = \OC::$APPSROOTS; foreach($appFolders as $key => $appFolder) { $appFolders[$key] = rtrim($appFolder['path'], '/'); } $excludedFolders = [ rtrim($root . '/data', '/'), rtrim($root . '/themes', '/'), rtrim($root . '/config', '/'), rtrim($root . '/apps', '/'), rtrim($root . '/assets', '/'), rtrim($root . '/lost+found', '/'), // Ignore folders generated by updater since the updater is replaced // after the integrity check is run. // See https://github.com/owncloud/updater/issues/318#issuecomment-212497846 rtrim($root . '/updater', '/'), rtrim($root . '/_oc_upgrade', '/'), ]; $customDataDir = \OC::$server->getConfig()->getSystemValue('datadirectory', ''); if($customDataDir !== '') { $excludedFolders[] = rtrim($customDataDir, '/'); } $this->excludedFolders = array_merge($excludedFolders, $appFolders); } /** * @return bool */ public function accept() { return !in_array( $this->current()->getPathName(), $this->excludedFolders, true ); } } private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php 0000604 00000003345 15247130453 0021606 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\IntegrityCheck\Iterator; /** * Class ExcludeFileByNameFilterIterator provides a custom iterator which excludes * entries with the specified file name from the file list. * * @package OC\Integritycheck\Iterator */ class ExcludeFileByNameFilterIterator extends \RecursiveFilterIterator { /** * Array of excluded file names. Those are not scanned by the integrity checker. * This is used to exclude files which administrators could upload by mistakes * such as .DS_Store files. * * @var array */ private $excludedFilenames = [ '.DS_Store', // Mac OS X 'Thumbs.db', // Microsoft Windows '.directory', // Dolphin (KDE) '.webapp', // Gentoo/Funtoo & derivatives use a tool known as webapp-config to manager wep-apps. ]; /** * @return bool */ public function accept() { if($this->isDir()) { return true; } return !in_array( $this->current()->getFilename(), $this->excludedFilenames, true ); } } private/IntegrityCheck/Helpers/EnvironmentHelper.php 0000604 00000002360 15247130453 0016712 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\IntegrityCheck\Helpers; /** * Class EnvironmentHelper provides a non-static helper for access to static * variables such as \OC::$SERVERROOT. * * @package OC\IntegrityCheck\Helpers */ class EnvironmentHelper { /** * Provides \OC::$SERVERROOT * * @return string */ public function getServerRoot() { return rtrim(\OC::$SERVERROOT, '/'); } /** * Provides \OC_Util::getChannel() * * @return string */ public function getChannel() { return \OC_Util::getChannel(); } } private/IntegrityCheck/Helpers/FileAccessHelper.php 0000604 00000004102 15247130453 0016403 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\IntegrityCheck\Helpers; /** * Class FileAccessHelper provides a helper around file_get_contents and * file_put_contents * * @package OC\IntegrityCheck\Helpers */ class FileAccessHelper { /** * Wrapper around file_get_contents($filename, $data) * * @param string $filename * @return string|false */ public function file_get_contents($filename) { return file_get_contents($filename); } /** * Wrapper around file_exists($filename) * * @param string $filename * @return bool */ public function file_exists($filename) { return file_exists($filename); } /** * Wrapper around file_put_contents($filename, $data) * * @param string $filename * @param string $data * @return int * @throws \Exception */ public function file_put_contents($filename, $data) { $bytesWritten = @file_put_contents($filename, $data); if ($bytesWritten === false || $bytesWritten !== strlen($data)){ throw new \Exception('Failed to write into ' . $filename); } return $bytesWritten; } /** * @param string $path * @return bool */ public function is_writable($path) { return is_writable($path); } /** * @param string $path * @throws \Exception */ public function assertDirectoryExists($path) { if (!is_dir($path)) { throw new \Exception('Directory ' . $path . ' does not exist.'); } } } private/IntegrityCheck/Helpers/AppLocator.php 0000604 00000002741 15247130453 0015315 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\IntegrityCheck\Helpers; /** * Class AppLocator provides a non-static helper for OC_App::getPath($appId) * it is not possible to use IAppManager at this point as IAppManager has a * dependency on a running ownCloud. * * @package OC\IntegrityCheck\Helpers */ class AppLocator { /** * Provides \OC_App::getAppPath($appId) * * @param string $appId * @return string * @throws \Exception If the app cannot be found */ public function getAppPath($appId) { $path = \OC_App::getAppPath($appId); if($path === false) { throw new \Exception('App not found'); } return $path; } /** * Providers \OC_App::getAllApps() * * @return array */ public function getAllApps() { return \OC_App::getAllApps(); } } private/AppHelper.php 0000604 00000002647 15247130453 0010620 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; /** * Class AppHelper * @deprecated 8.1.0 */ class AppHelper implements \OCP\IHelper { /** * Gets the content of an URL by using CURL or a fallback if it is not * installed * @param string $url the url that should be fetched * @return string the content of the webpage * @deprecated 8.1.0 Use \OCP\IServerContainer::getHTTPClientService */ public function getUrlContent($url) { try { $client = \OC::$server->getHTTPClientService()->newClient(); $response = $client->get($url); return $response->getBody(); } catch (\Exception $e) { return false; } } } private/Encryption/Manager.php 0000604 00000016417 15247130453 0012444 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption; use OC\Encryption\Keys\Storage; use OC\Files\Filesystem; use OC\Files\View; use OC\Memcache\ArrayCache; use OC\ServiceUnavailableException; use OCP\Encryption\IEncryptionModule; use OCP\Encryption\IManager; use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; class Manager implements IManager { /** @var array */ protected $encryptionModules; /** @var IConfig */ protected $config; /** @var ILogger */ protected $logger; /** @var Il10n */ protected $l; /** @var View */ protected $rootView; /** @var Util */ protected $util; /** @var ArrayCache */ protected $arrayCache; /** * @param IConfig $config * @param ILogger $logger * @param IL10N $l10n * @param View $rootView * @param Util $util * @param ArrayCache $arrayCache */ public function __construct(IConfig $config, ILogger $logger, IL10N $l10n, View $rootView, Util $util, ArrayCache $arrayCache) { $this->encryptionModules = array(); $this->config = $config; $this->logger = $logger; $this->l = $l10n; $this->rootView = $rootView; $this->util = $util; $this->arrayCache = $arrayCache; } /** * Check if encryption is enabled * * @return bool true if enabled, false if not */ public function isEnabled() { $installed = $this->config->getSystemValue('installed', false); if (!$installed) { return false; } $enabled = $this->config->getAppValue('core', 'encryption_enabled', 'no'); return $enabled === 'yes'; } /** * check if new encryption is ready * * @return bool * @throws ServiceUnavailableException */ public function isReady() { // check if we are still in transit between the old and the new encryption $oldEncryption = $this->config->getAppValue('files_encryption', 'installed_version'); if (!empty($oldEncryption)) { $warning = 'Installation is in transit between the old Encryption (ownCloud <= 8.0) and the new encryption. Please enable the "Default encryption module" and run \'occ encryption:migrate\''; $this->logger->warning($warning); return false; } if ($this->isKeyStorageReady() === false) { throw new ServiceUnavailableException('Key Storage is not ready'); } return true; } /** * @param string $user */ public function isReadyForUser($user) { if (!$this->isReady()) { return false; } foreach ($this->getEncryptionModules() as $module) { /** @var IEncryptionModule $m */ $m = call_user_func($module['callback']); if (!$m->isReadyForUser($user)) { return false; } } return true; } /** * Registers an callback function which must return an encryption module instance * * @param string $id * @param string $displayName * @param callable $callback * @throws Exceptions\ModuleAlreadyExistsException */ public function registerEncryptionModule($id, $displayName, callable $callback) { if (isset($this->encryptionModules[$id])) { throw new Exceptions\ModuleAlreadyExistsException($id, $displayName); } $this->encryptionModules[$id] = [ 'id' => $id, 'displayName' => $displayName, 'callback' => $callback, ]; $defaultEncryptionModuleId = $this->getDefaultEncryptionModuleId(); if (empty($defaultEncryptionModuleId)) { $this->setDefaultEncryptionModule($id); } } /** * Unregisters an encryption module * * @param string $moduleId */ public function unregisterEncryptionModule($moduleId) { unset($this->encryptionModules[$moduleId]); } /** * get a list of all encryption modules * * @return array [id => ['id' => $id, 'displayName' => $displayName, 'callback' => callback]] */ public function getEncryptionModules() { return $this->encryptionModules; } /** * get a specific encryption module * * @param string $moduleId * @return IEncryptionModule * @throws Exceptions\ModuleDoesNotExistsException */ public function getEncryptionModule($moduleId = '') { if (!empty($moduleId)) { if (isset($this->encryptionModules[$moduleId])) { return call_user_func($this->encryptionModules[$moduleId]['callback']); } else { $message = "Module with ID: $moduleId does not exist."; $hint = $this->l->t('Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator.', [$moduleId]); throw new Exceptions\ModuleDoesNotExistsException($message, $hint); } } else { return $this->getDefaultEncryptionModule(); } } /** * get default encryption module * * @return \OCP\Encryption\IEncryptionModule * @throws Exceptions\ModuleDoesNotExistsException */ protected function getDefaultEncryptionModule() { $defaultModuleId = $this->getDefaultEncryptionModuleId(); if (!empty($defaultModuleId)) { if (isset($this->encryptionModules[$defaultModuleId])) { return call_user_func($this->encryptionModules[$defaultModuleId]['callback']); } else { $message = 'Default encryption module not loaded'; throw new Exceptions\ModuleDoesNotExistsException($message); } } else { $message = 'No default encryption module defined'; throw new Exceptions\ModuleDoesNotExistsException($message); } } /** * set default encryption module Id * * @param string $moduleId * @return bool */ public function setDefaultEncryptionModule($moduleId) { try { $this->getEncryptionModule($moduleId); } catch (\Exception $e) { return false; } $this->config->setAppValue('core', 'default_encryption_module', $moduleId); return true; } /** * get default encryption module Id * * @return string */ public function getDefaultEncryptionModuleId() { return $this->config->getAppValue('core', 'default_encryption_module'); } /** * Add storage wrapper */ public function setupStorage() { // If encryption is disabled and there are no loaded modules it makes no sense to load the wrapper if (!empty($this->encryptionModules) || $this->isEnabled()) { $encryptionWrapper = new EncryptionWrapper($this->arrayCache, $this, $this->logger); Filesystem::addStorageWrapper('oc_encryption', array($encryptionWrapper, 'wrapStorage'), 2); } } /** * check if key storage is ready * * @return bool */ protected function isKeyStorageReady() { $rootDir = $this->util->getKeyStorageRoot(); // the default root is always valid if ($rootDir === '') { return true; } // check if key storage is mounted correctly if ($this->rootView->file_exists($rootDir . '/' . Storage::KEY_STORAGE_MARKER)) { return true; } return false; } } private/Encryption/Keys/Storage.php 0000604 00000023254 15247130453 0013406 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Keys; use OC\Encryption\Util; use OC\Files\Filesystem; use OC\Files\View; use OCP\Encryption\Keys\IStorage; use OC\User\NoUserException; class Storage implements IStorage { // hidden file which indicate that the folder is a valid key storage const KEY_STORAGE_MARKER = '.oc_key_storage'; /** @var View */ private $view; /** @var Util */ private $util; // base dir where all the file related keys are stored /** @var string */ private $keys_base_dir; // root of the key storage default is empty which means that we use the data folder /** @var string */ private $root_dir; /** @var string */ private $encryption_base_dir; /** @var string */ private $backup_base_dir; /** @var array */ private $keyCache = []; /** * @param View $view * @param Util $util */ public function __construct(View $view, Util $util) { $this->view = $view; $this->util = $util; $this->encryption_base_dir = '/files_encryption'; $this->keys_base_dir = $this->encryption_base_dir .'/keys'; $this->backup_base_dir = $this->encryption_base_dir .'/backup'; $this->root_dir = $this->util->getKeyStorageRoot(); } /** * @inheritdoc */ public function getUserKey($uid, $keyId, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid); return $this->getKey($path); } /** * @inheritdoc */ public function getFileKey($path, $keyId, $encryptionModuleId) { $realFile = $this->util->stripPartialFileExtension($path); $keyDir = $this->getFileKeyDir($encryptionModuleId, $realFile); $key = $this->getKey($keyDir . $keyId); if ($key === '' && $realFile !== $path) { // Check if the part file has keys and use them, if no normal keys // exist. This is required to fix copyBetweenStorage() when we // rename a .part file over storage borders. $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); $key = $this->getKey($keyDir . $keyId); } return $key; } /** * @inheritdoc */ public function getSystemUserKey($keyId, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null); return $this->getKey($path); } /** * @inheritdoc */ public function setUserKey($uid, $keyId, $key, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid); return $this->setKey($path, $key); } /** * @inheritdoc */ public function setFileKey($path, $keyId, $key, $encryptionModuleId) { $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); return $this->setKey($keyDir . $keyId, $key); } /** * @inheritdoc */ public function setSystemUserKey($keyId, $key, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null); return $this->setKey($path, $key); } /** * @inheritdoc */ public function deleteUserKey($uid, $keyId, $encryptionModuleId) { try { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid); return !$this->view->file_exists($path) || $this->view->unlink($path); } catch (NoUserException $e) { // this exception can come from initMountPoints() from setupUserMounts() // for a deleted user. // // It means, that: // - we are not running in alternative storage mode because we don't call // initMountPoints() in that mode // - the keys were in the user's home but since the user was deleted, the // user's home is gone and so are the keys // // So there is nothing to do, just ignore. } } /** * @inheritdoc */ public function deleteFileKey($path, $keyId, $encryptionModuleId) { $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); return !$this->view->file_exists($keyDir . $keyId) || $this->view->unlink($keyDir . $keyId); } /** * @inheritdoc */ public function deleteAllFileKeys($path) { $keyDir = $this->getFileKeyDir('', $path); return !$this->view->file_exists($keyDir) || $this->view->deleteAll($keyDir); } /** * @inheritdoc */ public function deleteSystemUserKey($keyId, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null); return !$this->view->file_exists($path) || $this->view->unlink($path); } /** * construct path to users key * * @param string $encryptionModuleId * @param string $keyId * @param string $uid * @return string */ protected function constructUserKeyPath($encryptionModuleId, $keyId, $uid) { if ($uid === null) { $path = $this->root_dir . '/' . $this->encryption_base_dir . '/' . $encryptionModuleId . '/' . $keyId; } else { $path = $this->root_dir . '/' . $uid . $this->encryption_base_dir . '/' . $encryptionModuleId . '/' . $uid . '.' . $keyId; } return \OC\Files\Filesystem::normalizePath($path); } /** * read key from hard disk * * @param string $path to key * @return string */ private function getKey($path) { $key = ''; if ($this->view->file_exists($path)) { if (isset($this->keyCache[$path])) { $key = $this->keyCache[$path]; } else { $key = $this->view->file_get_contents($path); $this->keyCache[$path] = $key; } } return $key; } /** * write key to disk * * * @param string $path path to key directory * @param string $key key * @return bool */ private function setKey($path, $key) { $this->keySetPreparation(dirname($path)); $result = $this->view->file_put_contents($path, $key); if (is_int($result) && $result > 0) { $this->keyCache[$path] = $key; return true; } return false; } /** * get path to key folder for a given file * * @param string $encryptionModuleId * @param string $path path to the file, relative to data/ * @return string */ private function getFileKeyDir($encryptionModuleId, $path) { list($owner, $filename) = $this->util->getUidAndFilename($path); // in case of system wide mount points the keys are stored directly in the data directory if ($this->util->isSystemWideMountPoint($filename, $owner)) { $keyPath = $this->root_dir . '/' . $this->keys_base_dir . $filename . '/'; } else { $keyPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $filename . '/'; } return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false); } /** * move keys if a file was renamed * * @param string $source * @param string $target * @return boolean */ public function renameKeys($source, $target) { $sourcePath = $this->getPathToKeys($source); $targetPath = $this->getPathToKeys($target); if ($this->view->file_exists($sourcePath)) { $this->keySetPreparation(dirname($targetPath)); $this->view->rename($sourcePath, $targetPath); return true; } return false; } /** * copy keys if a file was renamed * * @param string $source * @param string $target * @return boolean */ public function copyKeys($source, $target) { $sourcePath = $this->getPathToKeys($source); $targetPath = $this->getPathToKeys($target); if ($this->view->file_exists($sourcePath)) { $this->keySetPreparation(dirname($targetPath)); $this->view->copy($sourcePath, $targetPath); return true; } return false; } /** * backup keys of a given encryption module * * @param string $encryptionModuleId * @param string $purpose * @param string $uid * @return bool * @since 12.0.0 */ public function backupUserKeys($encryptionModuleId, $purpose, $uid) { $source = $uid . $this->encryption_base_dir . '/' . $encryptionModuleId; $backupDir = $uid . $this->backup_base_dir; if (!$this->view->file_exists($backupDir)) { $this->view->mkdir($backupDir); } $backupDir = $backupDir . '/' . $purpose . '.' . $encryptionModuleId . '.' . $this->getTimestamp(); $this->view->mkdir($backupDir); return $this->view->copy($source, $backupDir); } /** * get the current timestamp * * @return int */ protected function getTimestamp() { return time(); } /** * get system wide path and detect mount points * * @param string $path * @return string */ protected function getPathToKeys($path) { list($owner, $relativePath) = $this->util->getUidAndFilename($path); $systemWideMountPoint = $this->util->isSystemWideMountPoint($relativePath, $owner); if ($systemWideMountPoint) { $systemPath = $this->root_dir . '/' . $this->keys_base_dir . $relativePath . '/'; } else { $systemPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $relativePath . '/'; } return Filesystem::normalizePath($systemPath, false); } /** * Make preparations to filesystem for saving a key file * * @param string $path relative to the views root */ protected function keySetPreparation($path) { // If the file resides within a subdirectory, create it if (!$this->view->file_exists($path)) { $sub_dirs = explode('/', ltrim($path, '/')); $dir = ''; foreach ($sub_dirs as $sub_dir) { $dir .= '/' . $sub_dir; if (!$this->view->is_dir($dir)) { $this->view->mkdir($dir); } } } } } private/Encryption/File.php 0000604 00000007327 15247130453 0011751 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption; use OC\Cache\CappedMemoryCache; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Share\IManager; class File implements \OCP\Encryption\IFile { /** @var Util */ protected $util; /** @var IRootFolder */ private $rootFolder; /** @var IManager */ private $shareManager; /** * cache results of already checked folders * * @var array */ protected $cache; public function __construct(Util $util, IRootFolder $rootFolder, IManager $shareManager) { $this->util = $util; $this->cache = new CappedMemoryCache(); $this->rootFolder = $rootFolder; $this->shareManager = $shareManager; } /** * get list of users with access to the file * * @param string $path to the file * @return array ['users' => $uniqueUserIds, 'public' => $public] */ public function getAccessList($path) { // Make sure that a share key is generated for the owner too list($owner, $ownerPath) = $this->util->getUidAndFilename($path); // always add owner to the list of users with access to the file $userIds = array($owner); if (!$this->util->isFile($owner . '/' . $ownerPath)) { return array('users' => $userIds, 'public' => false); } $ownerPath = substr($ownerPath, strlen('/files')); $userFolder = $this->rootFolder->getUserFolder($owner); try { $file = $userFolder->get($ownerPath); } catch (NotFoundException $e) { $file = null; } $ownerPath = $this->util->stripPartialFileExtension($ownerPath); // first get the shares for the parent and cache the result so that we don't // need to check all parents for every file $parent = dirname($ownerPath); $parentNode = $userFolder->get($parent); if (isset($this->cache[$parent])) { $resultForParents = $this->cache[$parent]; } else { $resultForParents = $this->shareManager->getAccessList($parentNode); $this->cache[$parent] = $resultForParents; } $userIds = array_merge($userIds, $resultForParents['users']); $public = $resultForParents['public'] || $resultForParents['remote']; // Find out who, if anyone, is sharing the file if ($file !== null) { $resultForFile = $this->shareManager->getAccessList($file, false); $userIds = array_merge($userIds, $resultForFile['users']); $public = $resultForFile['public'] || $resultForFile['remote'] || $public; } // check if it is a group mount if (\OCP\App::isEnabled("files_external")) { $mounts = \OC_Mount_Config::getSystemMountPoints(); foreach ($mounts as $mount) { if ($mount['mountpoint'] == substr($ownerPath, 1, strlen($mount['mountpoint']))) { $mountedFor = $this->util->getUserWithAccessToMountPoint($mount['applicable']['users'], $mount['applicable']['groups']); $userIds = array_merge($userIds, $mountedFor); } } } // Remove duplicate UIDs $uniqueUserIds = array_unique($userIds); return array('users' => $uniqueUserIds, 'public' => $public); } } private/Encryption/Update.php 0000604 00000011420 15247130453 0012301 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption; use OC\Files\Filesystem; use \OC\Files\Mount; use \OC\Files\View; /** * update encrypted files, e.g. because a file was shared */ class Update { /** @var \OC\Files\View */ protected $view; /** @var \OC\Encryption\Util */ protected $util; /** @var \OC\Files\Mount\Manager */ protected $mountManager; /** @var \OC\Encryption\Manager */ protected $encryptionManager; /** @var string */ protected $uid; /** @var \OC\Encryption\File */ protected $file; /** * * @param \OC\Files\View $view * @param \OC\Encryption\Util $util * @param \OC\Files\Mount\Manager $mountManager * @param \OC\Encryption\Manager $encryptionManager * @param \OC\Encryption\File $file * @param string $uid */ public function __construct( View $view, Util $util, Mount\Manager $mountManager, Manager $encryptionManager, File $file, $uid ) { $this->view = $view; $this->util = $util; $this->mountManager = $mountManager; $this->encryptionManager = $encryptionManager; $this->file = $file; $this->uid = $uid; } /** * hook after file was shared * * @param array $params */ public function postShared($params) { if ($this->encryptionManager->isEnabled()) { if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { $path = Filesystem::getPath($params['fileSource']); list($owner, $ownerPath) = $this->getOwnerPath($path); $absPath = '/' . $owner . '/files/' . $ownerPath; $this->update($absPath); } } } /** * hook after file was unshared * * @param array $params */ public function postUnshared($params) { if ($this->encryptionManager->isEnabled()) { if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { $path = Filesystem::getPath($params['fileSource']); list($owner, $ownerPath) = $this->getOwnerPath($path); $absPath = '/' . $owner . '/files/' . $ownerPath; $this->update($absPath); } } } /** * inform encryption module that a file was restored from the trash bin, * e.g. to update the encryption keys * * @param array $params */ public function postRestore($params) { if ($this->encryptionManager->isEnabled()) { $path = Filesystem::normalizePath('/' . $this->uid . '/files/' . $params['filePath']); $this->update($path); } } /** * inform encryption module that a file was renamed, * e.g. to update the encryption keys * * @param array $params */ public function postRename($params) { $source = $params['oldpath']; $target = $params['newpath']; if( $this->encryptionManager->isEnabled() && dirname($source) !== dirname($target) ) { list($owner, $ownerPath) = $this->getOwnerPath($target); $absPath = '/' . $owner . '/files/' . $ownerPath; $this->update($absPath); } } /** * get owner and path relative to data/<owner>/files * * @param string $path path to file for current user * @return array ['owner' => $owner, 'path' => $path] * @throw \InvalidArgumentException */ protected function getOwnerPath($path) { $info = Filesystem::getFileInfo($path); $owner = Filesystem::getOwner($path); $view = new View('/' . $owner . '/files'); $path = $view->getPath($info->getId()); if ($path === null) { throw new \InvalidArgumentException('No file found for ' . $info->getId()); } return array($owner, $path); } /** * notify encryption module about added/removed users from a file/folder * * @param string $path relative to data/ * @throws Exceptions\ModuleDoesNotExistsException */ public function update($path) { // if a folder was shared, get a list of all (sub-)folders if ($this->view->is_dir($path)) { $allFiles = $this->util->getAllFiles($path); } else { $allFiles = array($path); } $encryptionModule = $this->encryptionManager->getEncryptionModule(); foreach ($allFiles as $file) { $usersSharing = $this->file->getAccessList($file); $encryptionModule->update($file, $this->uid, $usersSharing); } } } private/Encryption/Util.php 0000604 00000024451 15247130453 0012004 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Jan-Christoph Borchardt <hey@jancborchardt.net> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption; use OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException; use OC\Encryption\Exceptions\EncryptionHeaderToLargeException; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; use OC\Files\Filesystem; use OC\Files\View; use OCP\Encryption\IEncryptionModule; use OCP\IConfig; class Util { const HEADER_START = 'HBEGIN'; const HEADER_END = 'HEND'; const HEADER_PADDING_CHAR = '-'; const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module'; /** * block size will always be 8192 for a PHP stream * @see https://bugs.php.net/bug.php?id=21641 * @var integer */ protected $headerSize = 8192; /** * block size will always be 8192 for a PHP stream * @see https://bugs.php.net/bug.php?id=21641 * @var integer */ protected $blockSize = 8192; /** @var View */ protected $rootView; /** @var array */ protected $ocHeaderKeys; /** @var \OC\User\Manager */ protected $userManager; /** @var IConfig */ protected $config; /** @var array paths excluded from encryption */ protected $excludedPaths; /** @var \OC\Group\Manager $manager */ protected $groupManager; /** * * @param View $rootView * @param \OC\User\Manager $userManager * @param \OC\Group\Manager $groupManager * @param IConfig $config */ public function __construct( View $rootView, \OC\User\Manager $userManager, \OC\Group\Manager $groupManager, IConfig $config) { $this->ocHeaderKeys = [ self::HEADER_ENCRYPTION_MODULE_KEY ]; $this->rootView = $rootView; $this->userManager = $userManager; $this->groupManager = $groupManager; $this->config = $config; $this->excludedPaths[] = 'files_encryption'; $this->excludedPaths[] = 'appdata_' . $config->getSystemValue('instanceid', null); $this->excludedPaths[] = 'files_external'; } /** * read encryption module ID from header * * @param array $header * @return string * @throws ModuleDoesNotExistsException */ public function getEncryptionModuleId(array $header = null) { $id = ''; $encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY; if (isset($header[$encryptionModuleKey])) { $id = $header[$encryptionModuleKey]; } elseif (isset($header['cipher'])) { if (class_exists('\OCA\Encryption\Crypto\Encryption')) { // fall back to default encryption if the user migrated from // ownCloud <= 8.0 with the old encryption $id = \OCA\Encryption\Crypto\Encryption::ID; } else { throw new ModuleDoesNotExistsException('Default encryption module missing'); } } return $id; } /** * create header for encrypted file * * @param array $headerData * @param IEncryptionModule $encryptionModule * @return string * @throws EncryptionHeaderToLargeException if header has to many arguments * @throws EncryptionHeaderKeyExistsException if header key is already in use */ public function createHeader(array $headerData, IEncryptionModule $encryptionModule) { $header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':'; foreach ($headerData as $key => $value) { if (in_array($key, $this->ocHeaderKeys)) { throw new EncryptionHeaderKeyExistsException($key); } $header .= $key . ':' . $value . ':'; } $header .= self::HEADER_END; if (strlen($header) > $this->getHeaderSize()) { throw new EncryptionHeaderToLargeException(); } $paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT); return $paddedHeader; } /** * go recursively through a dir and collect all files and sub files. * * @param string $dir relative to the users files folder * @return array with list of files relative to the users files folder */ public function getAllFiles($dir) { $result = array(); $dirList = array($dir); while ($dirList) { $dir = array_pop($dirList); $content = $this->rootView->getDirectoryContent($dir); foreach ($content as $c) { if ($c->getType() === 'dir') { $dirList[] = $c->getPath(); } else { $result[] = $c->getPath(); } } } return $result; } /** * check if it is a file uploaded by the user stored in data/user/files * or a metadata file * * @param string $path relative to the data/ folder * @return boolean */ public function isFile($path) { $parts = explode('/', Filesystem::normalizePath($path), 4); if (isset($parts[2]) && $parts[2] === 'files') { return true; } return false; } /** * return size of encryption header * * @return integer */ public function getHeaderSize() { return $this->headerSize; } /** * return size of block read by a PHP stream * * @return integer */ public function getBlockSize() { return $this->blockSize; } /** * get the owner and the path for the file relative to the owners files folder * * @param string $path * @return array * @throws \BadMethodCallException */ public function getUidAndFilename($path) { $parts = explode('/', $path); $uid = ''; if (count($parts) > 2) { $uid = $parts[1]; } if (!$this->userManager->userExists($uid)) { throw new \BadMethodCallException( 'path needs to be relative to the system wide data folder and point to a user specific file' ); } $ownerPath = implode('/', array_slice($parts, 2)); return array($uid, Filesystem::normalizePath($ownerPath)); } /** * Remove .path extension from a file path * @param string $path Path that may identify a .part file * @return string File path without .part extension * @note this is needed for reusing keys */ public function stripPartialFileExtension($path) { $extension = pathinfo($path, PATHINFO_EXTENSION); if ( $extension === 'part') { $newLength = strlen($path) - 5; // 5 = strlen(".part") $fPath = substr($path, 0, $newLength); // if path also contains a transaction id, we remove it too $extension = pathinfo($fPath, PATHINFO_EXTENSION); if(substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId") $newLength = strlen($fPath) - strlen($extension) -1; $fPath = substr($fPath, 0, $newLength); } return $fPath; } else { return $path; } } public function getUserWithAccessToMountPoint($users, $groups) { $result = array(); if (in_array('all', $users)) { $result = \OCP\User::getUsers(); } else { $result = array_merge($result, $users); $groupManager = \OC::$server->getGroupManager(); foreach ($groups as $group) { $groupObject = $groupManager->get($group); if ($groupObject) { $foundUsers = $groupObject->searchUsers('', -1, 0); $userIds = []; foreach ($foundUsers as $user) { $userIds[] = $user->getUID(); } $result = array_merge($result, $userIds); } } } return $result; } /** * check if the file is stored on a system wide mount point * @param string $path relative to /data/user with leading '/' * @param string $uid * @return boolean */ public function isSystemWideMountPoint($path, $uid) { if (\OCP\App::isEnabled("files_external")) { $mounts = \OC_Mount_Config::getSystemMountPoints(); foreach ($mounts as $mount) { if (strpos($path, '/files/' . $mount['mountpoint']) === 0) { if ($this->isMountPointApplicableToUser($mount, $uid)) { return true; } } } } return false; } /** * check if mount point is applicable to user * * @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups'] * @param string $uid * @return boolean */ private function isMountPointApplicableToUser($mount, $uid) { $acceptedUids = array('all', $uid); // check if mount point is applicable for the user $intersection = array_intersect($acceptedUids, $mount['applicable']['users']); if (!empty($intersection)) { return true; } // check if mount point is applicable for group where the user is a member foreach ($mount['applicable']['groups'] as $gid) { if ($this->groupManager->isInGroup($uid, $gid)) { return true; } } return false; } /** * check if it is a path which is excluded by ownCloud from encryption * * @param string $path * @return boolean */ public function isExcluded($path) { $normalizedPath = Filesystem::normalizePath($path); $root = explode('/', $normalizedPath, 4); if (count($root) > 1) { // detect alternative key storage root $rootDir = $this->getKeyStorageRoot(); if ($rootDir !== '' && 0 === strpos( Filesystem::normalizePath($path), Filesystem::normalizePath($rootDir) ) ) { return true; } //detect system wide folders if (in_array($root[1], $this->excludedPaths)) { return true; } // detect user specific folders if ($this->userManager->userExists($root[1]) && in_array($root[2], $this->excludedPaths)) { return true; } } return false; } /** * check if recovery key is enabled for user * * @param string $uid * @return boolean */ public function recoveryEnabled($uid) { $enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0'); return ($enabled === '1') ? true : false; } /** * set new key storage root * * @param string $root new key store root relative to the data folder */ public function setKeyStorageRoot($root) { $this->config->setAppValue('core', 'encryption_key_storage_root', $root); } /** * get key storage root * * @return string key storage root */ public function getKeyStorageRoot() { return $this->config->getAppValue('core', 'encryption_key_storage_root', ''); } } private/Encryption/Exceptions/ModuleDoesNotExistsException.php 0000604 00000001725 15247130453 0021007 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; class ModuleDoesNotExistsException extends GenericEncryptionException { } private/Encryption/Exceptions/EncryptionHeaderToLargeException.php 0000604 00000002060 15247130453 0021600 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Clark Tomlinson <fallen013@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; class EncryptionHeaderToLargeException extends GenericEncryptionException { public function __construct() { parent::__construct('max header size exceeded'); } } private/Encryption/Exceptions/ModuleAlreadyExistsException.php 0000604 00000002231 15247130453 0021006 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; class ModuleAlreadyExistsException extends GenericEncryptionException { /** * @param string $id * @param string $name */ public function __construct($id, $name) { parent::__construct('Id "' . $id . '" already used by encryption module "' . $name . '"'); } } private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php 0000604 00000002164 15247130453 0022200 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; class EncryptionHeaderKeyExistsException extends GenericEncryptionException { /** * @param string $key */ public function __construct($key) { parent::__construct('header key "'. $key . '" already reserved by ownCloud'); } } private/Encryption/Exceptions/DecryptionFailedException.php 0000604 00000001721 15247130453 0020307 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Clark Tomlinson <fallen013@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; class DecryptionFailedException extends GenericEncryptionException { } private/Encryption/Exceptions/EmptyEncryptionDataException.php 0000604 00000001723 15247130453 0021027 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Clark Tomlinson <fallen013@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; class EmptyEncryptionDataException extends GenericEncryptionException{ } private/Encryption/Exceptions/EncryptionFailedException.php 0000604 00000001720 15247130453 0020320 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Clark Tomlinson <fallen013@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; class EncryptionFailedException extends GenericEncryptionException{ } private/Encryption/Exceptions/UnknownCipherException.php 0000604 00000001716 15247130453 0017660 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Clark Tomlinson <fallen013@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; class UnknownCipherException extends GenericEncryptionException { } private/Encryption/HookManager.php 0000604 00000003561 15247130453 0013261 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption; use OC\Files\Filesystem; use OC\Files\View; class HookManager { /** * @var Update */ private static $updater; public static function postShared($params) { self::getUpdate()->postShared($params); } public static function postUnshared($params) { self::getUpdate()->postUnshared($params); } public static function postRename($params) { self::getUpdate()->postRename($params); } public static function postRestore($params) { self::getUpdate()->postRestore($params); } /** * @return Update */ private static function getUpdate() { if (is_null(self::$updater)) { $user = \OC::$server->getUserSession()->getUser(); $uid = ''; if ($user) { $uid = $user->getUID(); } self::$updater = new Update( new View(), new Util( new View(), \OC::$server->getUserManager(), \OC::$server->getGroupManager(), \OC::$server->getConfig()), Filesystem::getMountManager(), \OC::$server->getEncryptionManager(), \OC::$server->getEncryptionFilesHelper(), $uid ); } return self::$updater; } } private/Encryption/EncryptionWrapper.php 0000604 00000005616 15247130453 0014564 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption; use OC\Memcache\ArrayCache; use OC\Files\Filesystem; use OC\Files\Storage\Wrapper\Encryption; use OCP\Files\Mount\IMountPoint; use OC\Files\View; use OCP\Files\Storage; use OCP\ILogger; /** * Class EncryptionWrapper * * applies the encryption storage wrapper * * @package OC\Encryption */ class EncryptionWrapper { /** @var ArrayCache */ private $arrayCache; /** @var Manager */ private $manager; /** @var ILogger */ private $logger; /** * EncryptionWrapper constructor. * * @param ArrayCache $arrayCache * @param Manager $manager * @param ILogger $logger */ public function __construct(ArrayCache $arrayCache, Manager $manager, ILogger $logger ) { $this->arrayCache = $arrayCache; $this->manager = $manager; $this->logger = $logger; } /** * Wraps the given storage when it is not a shared storage * * @param string $mountPoint * @param Storage $storage * @param IMountPoint $mount * @return Encryption|Storage */ public function wrapStorage($mountPoint, Storage $storage, IMountPoint $mount) { $parameters = [ 'storage' => $storage, 'mountPoint' => $mountPoint, 'mount' => $mount ]; if (!$storage->instanceOfStorage('OCA\Files_Sharing\SharedStorage') && !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') && !$storage->instanceOfStorage('OC\Files\Storage\OwnCloud')) { $user = \OC::$server->getUserSession()->getUser(); $mountManager = Filesystem::getMountManager(); $uid = $user ? $user->getUID() : null; $fileHelper = \OC::$server->getEncryptionFilesHelper(); $keyStorage = \OC::$server->getEncryptionKeyStorage(); $util = new Util( new View(), \OC::$server->getUserManager(), \OC::$server->getGroupManager(), \OC::$server->getConfig() ); $update = new Update( new View(), $util, Filesystem::getMountManager(), $this->manager, $fileHelper, $uid ); return new Encryption( $parameters, $this->manager, $util, $this->logger, $fileHelper, $uid, $keyStorage, $update, $mountManager, $this->arrayCache ); } else { return $storage; } } } private/Encryption/DecryptAll.php 0000604 00000017202 15247130453 0013126 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Christian Jürges <christian@eqipe.ch> * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Encryption; use OC\Encryption\Exceptions\DecryptionFailedException; use OC\Files\View; use \OCP\Encryption\IEncryptionModule; use OCP\IUserManager; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class DecryptAll { /** @var OutputInterface */ protected $output; /** @var InputInterface */ protected $input; /** @var Manager */ protected $encryptionManager; /** @var IUserManager */ protected $userManager; /** @var View */ protected $rootView; /** @var array files which couldn't be decrypted */ protected $failed; /** * @param Manager $encryptionManager * @param IUserManager $userManager * @param View $rootView */ public function __construct( Manager $encryptionManager, IUserManager $userManager, View $rootView ) { $this->encryptionManager = $encryptionManager; $this->userManager = $userManager; $this->rootView = $rootView; $this->failed = []; } /** * start to decrypt all files * * @param InputInterface $input * @param OutputInterface $output * @param string $user which users data folder should be decrypted, default = all users * @return bool * @throws \Exception */ public function decryptAll(InputInterface $input, OutputInterface $output, $user = '') { $this->input = $input; $this->output = $output; if ($user !== '' && $this->userManager->userExists($user) === false) { $this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again'); return false; } $this->output->writeln('prepare encryption modules...'); if ($this->prepareEncryptionModules($user) === false) { return false; } $this->output->writeln(' done.'); $this->decryptAllUsersFiles($user); if (empty($this->failed)) { $this->output->writeln('all files could be decrypted successfully!'); } else { $this->output->writeln('Files for following users couldn\'t be decrypted, '); $this->output->writeln('maybe the user is not set up in a way that supports this operation: '); foreach ($this->failed as $uid => $paths) { $this->output->writeln(' ' . $uid); } $this->output->writeln(''); } return true; } /** * prepare encryption modules to perform the decrypt all function * * @param $user * @return bool */ protected function prepareEncryptionModules($user) { // prepare all encryption modules for decrypt all $encryptionModules = $this->encryptionManager->getEncryptionModules(); foreach ($encryptionModules as $moduleDesc) { /** @var IEncryptionModule $module */ $module = call_user_func($moduleDesc['callback']); $this->output->writeln(''); $this->output->writeln('Prepare "' . $module->getDisplayName() . '"'); $this->output->writeln(''); if ($module->prepareDecryptAll($this->input, $this->output, $user) === false) { $this->output->writeln('Module "' . $moduleDesc['displayName'] . '" does not support the functionality to decrypt all files again or the initialization of the module failed!'); return false; } } return true; } /** * iterate over all user and encrypt their files * * @param string $user which users files should be decrypted, default = all users */ protected function decryptAllUsersFiles($user = '') { $this->output->writeln("\n"); $userList = []; if ($user === '') { $fetchUsersProgress = new ProgressBar($this->output); $fetchUsersProgress->setFormat(" %message% \n [%bar%]"); $fetchUsersProgress->start(); $fetchUsersProgress->setMessage("Fetch list of users..."); $fetchUsersProgress->advance(); foreach ($this->userManager->getBackends() as $backend) { $limit = 500; $offset = 0; do { $users = $backend->getUsers('', $limit, $offset); foreach ($users as $user) { $userList[] = $user; } $offset += $limit; $fetchUsersProgress->advance(); } while (count($users) >= $limit); $fetchUsersProgress->setMessage("Fetch list of users... finished"); $fetchUsersProgress->finish(); } } else { $userList[] = $user; } $this->output->writeln("\n\n"); $progress = new ProgressBar($this->output); $progress->setFormat(" %message% \n [%bar%]"); $progress->start(); $progress->setMessage("starting to decrypt files..."); $progress->advance(); $numberOfUsers = count($userList); $userNo = 1; foreach ($userList as $uid) { $userCount = "$uid ($userNo of $numberOfUsers)"; $this->decryptUsersFiles($uid, $progress, $userCount); $userNo++; } $progress->setMessage("starting to decrypt files... finished"); $progress->finish(); $this->output->writeln("\n\n"); } /** * encrypt files from the given user * * @param string $uid * @param ProgressBar $progress * @param string $userCount */ protected function decryptUsersFiles($uid, ProgressBar $progress, $userCount) { $this->setupUserFS($uid); $directories = array(); $directories[] = '/' . $uid . '/files'; while ($root = array_pop($directories)) { $content = $this->rootView->getDirectoryContent($root); foreach ($content as $file) { // only decrypt files owned by the user if($file->getStorage()->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) { continue; } $path = $root . '/' . $file['name']; if ($this->rootView->is_dir($path)) { $directories[] = $path; continue; } else { try { $progress->setMessage("decrypt files for user $userCount: $path"); $progress->advance(); if ($file->isEncrypted() === false) { $progress->setMessage("decrypt files for user $userCount: $path (already decrypted)"); $progress->advance(); } else { if ($this->decryptFile($path) === false) { $progress->setMessage("decrypt files for user $userCount: $path (already decrypted)"); $progress->advance(); } } } catch (\Exception $e) { if (isset($this->failed[$uid])) { $this->failed[$uid][] = $path; } else { $this->failed[$uid] = [$path]; } } } } } } /** * encrypt file * * @param string $path * @return bool */ protected function decryptFile($path) { $source = $path; $target = $path . '.decrypted.' . $this->getTimestamp(); try { $this->rootView->copy($source, $target); $this->rootView->rename($target, $source); } catch (DecryptionFailedException $e) { if ($this->rootView->file_exists($target)) { $this->rootView->unlink($target); } return false; } return true; } /** * get current timestamp * * @return int */ protected function getTimestamp() { return time(); } /** * setup user file system * * @param string $uid */ protected function setupUserFS($uid) { \OC_Util::tearDownFS(); \OC_Util::setupFS($uid); } } private/Contacts/ContactsMenu/Entry.php 0000604 00000006744 15247130453 0014224 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Contacts\ContactsMenu; use OCP\Contacts\ContactsMenu\IAction; use OCP\Contacts\ContactsMenu\IEntry; class Entry implements IEntry { /** @var string|int|null */ private $id = null; /** @var string */ private $fullName = ''; /** @var string[] */ private $emailAddresses = []; /** @var string|null */ private $avatar; /** @var IAction[] */ private $actions = []; /** @var array */ private $properties = []; /** * @param string $id */ public function setId($id) { $this->id = $id; } /** * @param string $displayName */ public function setFullName($displayName) { $this->fullName = $displayName; } /** * @return string */ public function getFullName() { return $this->fullName; } /** * @param string $address */ public function addEMailAddress($address) { $this->emailAddresses[] = $address; } /** * @return string */ public function getEMailAddresses() { return $this->emailAddresses; } /** * @param string $avatar */ public function setAvatar($avatar) { $this->avatar = $avatar; } /** * @return string */ public function getAvatar() { return $this->avatar; } /** * @param IAction $action */ public function addAction(IAction $action) { $this->actions[] = $action; $this->sortActions(); } /** * @return IAction[] */ public function getActions() { return $this->actions; } /** * sort the actions by priority and name */ private function sortActions() { usort($this->actions, function(IAction $action1, IAction $action2) { $prio1 = $action1->getPriority(); $prio2 = $action2->getPriority(); if ($prio1 === $prio2) { // Ascending order for same priority return strcasecmp($action1->getName(), $action2->getName()); } // Descending order when priority differs return $prio2 - $prio1; }); } /** * @param array $contact key-value array containing additional properties */ public function setProperties(array $contact) { $this->properties = $contact; } /** * @param string $key * @return mixed */ public function getProperty($key) { if (!isset($this->properties[$key])) { return null; } return $this->properties[$key]; } /** * @return array */ public function jsonSerialize() { $topAction = !empty($this->actions) ? $this->actions[0]->jsonSerialize() : null; $otherActions = array_map(function(IAction $action) { return $action->jsonSerialize(); }, array_slice($this->actions, 1)); return [ 'id' => $this->id, 'fullName' => $this->fullName, 'avatar' => $this->getAvatar(), 'topAction' => $topAction, 'actions' => $otherActions, 'lastMessage' => '', ]; } } private/Contacts/ContactsMenu/Manager.php 0000604 00000005574 15247130453 0014475 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Contacts\ContactsMenu; use OCP\App\IAppManager; use OCP\Contacts\ContactsMenu\IEntry; use OCP\IUser; class Manager { /** @var ContactsStore */ private $store; /** @var ActionProviderStore */ private $actionProviderStore; /** @var IAppManager */ private $appManager; /** * @param ContactsStore $store * @param ActionProviderStore $actionProviderStore * @param IAppManager $appManager */ public function __construct(ContactsStore $store, ActionProviderStore $actionProviderStore, IAppManager $appManager) { $this->store = $store; $this->actionProviderStore = $actionProviderStore; $this->appManager = $appManager; } /** * @param IUser $user * @param string $filter * @return array */ public function getEntries(IUser $user, $filter) { $entries = $this->store->getContacts($user, $filter); $sortedEntries = $this->sortEntries($entries); $topEntries = array_slice($sortedEntries, 0, 25); $this->processEntries($topEntries, $user); $contactsEnabled = $this->appManager->isEnabledForUser('contacts', $user); return [ 'contacts' => $topEntries, 'contactsAppEnabled' => $contactsEnabled, ]; } /** * @param IUser $user * @param integer $shareType * @param string $shareWith * @return IEntry */ public function findOne(IUser $user, $shareType, $shareWith) { $entry = $this->store->findOne($user, $shareType, $shareWith); if ($entry) { $this->processEntries([$entry], $user); } return $entry; } /** * @param IEntry[] $entries * @return IEntry[] */ private function sortEntries(array $entries) { usort($entries, function(IEntry $entryA, IEntry $entryB) { return strcasecmp($entryA->getFullName(), $entryB->getFullName()); }); return $entries; } /** * @param IEntry[] $entries * @param IUser $user */ private function processEntries(array $entries, IUser $user) { $providers = $this->actionProviderStore->getProviders($user); foreach ($entries as $entry) { foreach ($providers as $provider) { $provider->process($entry); } } } } private/Contacts/ContactsMenu/ActionFactory.php 0000604 00000003147 15247130453 0015662 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Contacts\ContactsMenu; use OC\Contacts\ContactsMenu\Actions\LinkAction; use OCP\Contacts\ContactsMenu\IActionFactory; use OCP\Contacts\ContactsMenu\ILinkAction; class ActionFactory implements IActionFactory { /** * @param string $icon * @param string $name * @param string $href * @return ILinkAction */ public function newLinkAction($icon, $name, $href) { $action = new LinkAction(); $action->setName($name); $action->setIcon($icon); $action->setHref($href); return $action; } /** * @param string $icon * @param string $name * @param string $email * @return ILinkAction */ public function newEMailAction($icon, $name, $email) { return $this->newLinkAction($icon, $name, 'mailto:' . urlencode($email)); } } private/Contacts/ContactsMenu/Providers/EMailProvider.php 0000604 00000003563 15247130453 0017576 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Contacts\ContactsMenu\Providers; use OCP\Contacts\ContactsMenu\IActionFactory; use OCP\Contacts\ContactsMenu\IEntry; use OCP\Contacts\ContactsMenu\IProvider; use OCP\IURLGenerator; class EMailProvider implements IProvider { /** @var IActionFactory */ private $actionFactory; /** @var IURLGenerator */ private $urlGenerator; /** * @param IActionFactory $actionFactory * @param IURLGenerator $urlGenerator */ public function __construct(IActionFactory $actionFactory, IURLGenerator $urlGenerator) { $this->actionFactory = $actionFactory; $this->urlGenerator = $urlGenerator; } /** * @param IEntry $entry */ public function process(IEntry $entry) { $iconUrl = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'actions/mail.svg')); foreach ($entry->getEMailAddresses() as $address) { if (empty($address)) { // Skip continue; } $action = $this->actionFactory->newEMailAction($iconUrl, $address, $address); $entry->addAction($action); } } } private/Contacts/ContactsMenu/ActionProviderStore.php 0000604 00000005737 15247130453 0017071 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Contacts\ContactsMenu; use Exception; use OC\App\AppManager; use OC\Contacts\ContactsMenu\Providers\EMailProvider; use OCP\AppFramework\QueryException; use OCP\Contacts\ContactsMenu\IProvider; use OCP\ILogger; use OCP\IServerContainer; use OCP\IUser; class ActionProviderStore { /** @var IServerContainer */ private $serverContainer; /** @var AppManager */ private $appManager; /** @var ILogger */ private $logger; /** * @param IServerContainer $serverContainer * @param AppManager $appManager * @param ILogger $logger */ public function __construct(IServerContainer $serverContainer, AppManager $appManager, ILogger $logger) { $this->serverContainer = $serverContainer; $this->appManager = $appManager; $this->logger = $logger; } /** * @param IUser $user * @return IProvider[] * @throws Exception */ public function getProviders(IUser $user) { $appClasses = $this->getAppProviderClasses($user); $providerClasses = $this->getServerProviderClasses(); $allClasses = array_merge($providerClasses, $appClasses); $providers = []; foreach ($allClasses as $class) { try { $providers[] = $this->serverContainer->query($class); } catch (QueryException $ex) { $this->logger->logException($ex, [ 'message' => "Could not load contacts menu action provider $class", 'app' => 'core', ]); throw new Exception("Could not load contacts menu action provider"); } } return $providers; } /** * @return string[] */ private function getServerProviderClasses() { return [ EMailProvider::class, ]; } /** * @param IUser $user * @return string[] */ private function getAppProviderClasses(IUser $user) { return array_reduce($this->appManager->getEnabledAppsForUser($user), function($all, $appId) { $info = $this->appManager->getAppInfo($appId); if (!isset($info['contactsmenu']) || !isset($info['contactsmenu'])) { // Nothing to add return $all; } $providers = array_reduce($info['contactsmenu'], function($all, $provider) { return array_merge($all, [$provider]); }, []); return array_merge($all, $providers); }, []); } } private/Contacts/ContactsMenu/Actions/LinkAction.php 0000604 00000003731 15247130453 0016547 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Contacts\ContactsMenu\Actions; use OCP\Contacts\ContactsMenu\ILinkAction; class LinkAction implements ILinkAction { /** @var string */ private $icon; /** @var string */ private $name; /** @var string */ private $href; /** @var int */ private $priority = 10; /** * @param string $icon absolute URI to an icon */ public function setIcon($icon) { $this->icon = $icon; } /** * @param string $name */ public function setName($name) { $this->name = $name; } /** * @return string */ public function getName() { return $this->name; } /** * @param int $priority */ public function setPriority($priority) { $this->priority = $priority; } /** * @return int */ public function getPriority() { return $this->priority; } /** * @param string $href */ public function setHref($href) { $this->href = $href; } /** * @return string */ public function getHref() { return $this->href; } /** * @return array */ public function jsonSerialize() { return [ 'title' => $this->name, 'icon' => $this->icon, 'hyperlink' => $this->href, ]; } } private/Contacts/ContactsMenu/ContactsStore.php 0000604 00000006635 15247130453 0015715 0 ustar 00 <?php /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Contacts\ContactsMenu; use OCP\Contacts\ContactsMenu\IEntry; use OCP\Contacts\IManager; use OCP\IUser; class ContactsStore { /** @var IManager */ private $contactsManager; /** * @param IManager $contactsManager */ public function __construct(IManager $contactsManager) { $this->contactsManager = $contactsManager; } /** * @param IUser $user * @param string|null $filter * @return IEntry[] */ public function getContacts(IUser $user, $filter) { $allContacts = $this->contactsManager->search($filter ?: '', [ 'FN', ]); $self = $user->getUID(); $entries = array_map(function(array $contact) { return $this->contactArrayToEntry($contact); }, $allContacts); return array_filter($entries, function(IEntry $entry) use ($self) { return $entry->getProperty('UID') !== $self; }); } /** * @param IUser $user * @param integer $shareType * @param string $shareWith * @return IEntry|null */ public function findOne(IUser $user, $shareType, $shareWith) { switch($shareType) { case 0: case 6: $filter = ['UID']; break; case 4: $filter = ['EMAIL']; break; default: return null; } $userId = $user->getUID(); $allContacts = $this->contactsManager->search($shareWith, $filter); $contacts = array_filter($allContacts, function($contact) use ($userId) { return $contact['UID'] !== $userId; }); $match = null; foreach ($contacts as $contact) { if ($shareType === 4 && isset($contact['EMAIL'])) { if (in_array($shareWith, $contact['EMAIL'])) { $match = $contact; break; } } if ($shareType === 0 || $shareType === 6) { if ($contact['UID'] === $shareWith && $contact['isLocalSystemBook'] === true) { $match = $contact; break; } } } return $match ? $this->contactArrayToEntry($match) : null; } /** * @param array $contact * @return Entry */ private function contactArrayToEntry(array $contact) { $entry = new Entry(); if (isset($contact['id'])) { $entry->setId($contact['id']); } if (isset($contact['FN'])) { $entry->setFullName($contact['FN']); } $avatarPrefix = "VALUE=uri:"; if (isset($contact['PHOTO']) && strpos($contact['PHOTO'], $avatarPrefix) === 0) { $entry->setAvatar(substr($contact['PHOTO'], strlen($avatarPrefix))); } if (isset($contact['EMAIL'])) { foreach ($contact['EMAIL'] as $email) { $entry->addEMailAddress($email); } } // Attach all other properties to the entry too because some // providers might make use of it. $entry->setProperties($contact); return $entry; } } private/Log/Rotate.php 0000604 00000003667 15247130453 0010722 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Log; /** * This rotates the current logfile to a new name, this way the total log usage * will stay limited and older entries are available for a while longer. * For more professional log management set the 'logfile' config to a different * location and manage that with your own tools. */ class Rotate extends \OC\BackgroundJob\Job { private $max_log_size; public function run($dummy) { $systemConfig = \OC::$server->getSystemConfig(); $logFile = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); $this->max_log_size = \OC::$server->getConfig()->getSystemValue('log_rotate_size', false); if ($this->max_log_size) { $filesize = @filesize($logFile); if ($filesize >= $this->max_log_size) { $this->rotate($logFile); } } } protected function rotate($logfile) { $rotatedLogfile = $logfile.'.1'; rename($logfile, $rotatedLogfile); $msg = 'Log file "'.$logfile.'" was over '.$this->max_log_size.' bytes, moved to "'.$rotatedLogfile.'"'; \OCP\Util::writeLog('OC\Log\Rotate', $msg, \OCP\Util::WARN); } } private/Log/File.php 0000604 00000013317 15247130453 0010334 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Georg Ehrke <georg@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Phiber2000 <phiber2000@gmx.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Pulzer <t.pulzer@kniel.de> * @author Vincent Petry <pvince81@owncloud.com> * @author Roger Szabo <roger.szabo@web.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Log; /** * logging utilities * * Log is saved at data/nextcloud.log (on default) */ class File { static protected $logFile; /** * Init class data */ public static function init() { $systemConfig = \OC::$server->getSystemConfig(); $defaultLogFile = $systemConfig->getValue("datadirectory", \OC::$SERVERROOT.'/data').'/nextcloud.log'; self::$logFile = $systemConfig->getValue("logfile", $defaultLogFile); /** * Fall back to default log file if specified logfile does not exist * and can not be created. */ if (!file_exists(self::$logFile)) { if(!is_writable(dirname(self::$logFile))) { self::$logFile = $defaultLogFile; } else { if(!touch(self::$logFile)) { self::$logFile = $defaultLogFile; } } } } /** * write a message in the log * @param string $app * @param string $message * @param int $level */ public static function write($app, $message, $level) { $config = \OC::$server->getSystemConfig(); // default to ISO8601 $format = $config->getValue('logdateformat', \DateTime::ATOM); $logTimeZone = $config->getValue('logtimezone', 'UTC'); try { $timezone = new \DateTimeZone($logTimeZone); } catch (\Exception $e) { $timezone = new \DateTimeZone('UTC'); } $time = \DateTime::createFromFormat("U.u", number_format(microtime(true), 4, ".", "")); if ($time === false) { $time = new \DateTime(null, $timezone); } else { // apply timezone if $time is created from UNIX timestamp $time->setTimezone($timezone); } $request = \OC::$server->getRequest(); $reqId = $request->getId(); $remoteAddr = $request->getRemoteAddress(); // remove username/passwords from URLs before writing the to the log file $time = $time->format($format); $url = ($request->getRequestUri() !== '') ? $request->getRequestUri() : '--'; $method = is_string($request->getMethod()) ? $request->getMethod() : '--'; if($config->getValue('installed', false)) { $user = (\OC_User::getUser()) ? \OC_User::getUser() : '--'; } else { $user = '--'; } $userAgent = $request->getHeader('User-Agent') ?: '--'; $version = $config->getValue('version', ''); $entry = compact( 'reqId', 'level', 'time', 'remoteAddr', 'user', 'app', 'method', 'url', 'message', 'userAgent', 'version' ); // PHP's json_encode only accept proper UTF-8 strings, loop over all // elements to ensure that they are properly UTF-8 compliant or convert // them manually. foreach($entry as $key => $value) { if(is_string($value)) { $testEncode = json_encode($value); if($testEncode === false) { $entry[$key] = utf8_encode($value); } } } $entry = json_encode($entry, JSON_PARTIAL_OUTPUT_ON_ERROR); $handle = @fopen(self::$logFile, 'a'); if ((fileperms(self::$logFile) & 0777) != 0640) { @chmod(self::$logFile, 0640); } if ($handle) { fwrite($handle, $entry."\n"); fclose($handle); } else { // Fall back to error_log error_log($entry); } if (php_sapi_name() === 'cli-server') { error_log($message, 4); } } /** * get entries from the log in reverse chronological order * @param int $limit * @param int $offset * @return array */ public static function getEntries($limit=50, $offset=0) { self::init(); $minLevel = \OC::$server->getSystemConfig()->getValue("loglevel", \OCP\Util::WARN); $entries = array(); $handle = @fopen(self::$logFile, 'rb'); if ($handle) { fseek($handle, 0, SEEK_END); $pos = ftell($handle); $line = ''; $entriesCount = 0; $lines = 0; // Loop through each character of the file looking for new lines while ($pos >= 0 && ($limit === null ||$entriesCount < $limit)) { fseek($handle, $pos); $ch = fgetc($handle); if ($ch == "\n" || $pos == 0) { if ($line != '') { // Add the first character if at the start of the file, // because it doesn't hit the else in the loop if ($pos == 0) { $line = $ch.$line; } $entry = json_decode($line); // Add the line as an entry if it is passed the offset and is equal or above the log level if ($entry->level >= $minLevel) { $lines++; if ($lines > $offset) { $entries[] = $entry; $entriesCount++; } } $line = ''; } } else { $line = $ch.$line; } $pos--; } fclose($handle); } return $entries; } /** * @return string */ public static function getLogFilePath() { return self::$logFile; } } private/Log/Syslog.php 0000604 00000003151 15247130453 0010730 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Log; class Syslog { static protected $levels = array( \OCP\Util::DEBUG => LOG_DEBUG, \OCP\Util::INFO => LOG_INFO, \OCP\Util::WARN => LOG_WARNING, \OCP\Util::ERROR => LOG_ERR, \OCP\Util::FATAL => LOG_CRIT, ); /** * Init class data */ public static function init() { openlog(\OC::$server->getSystemConfig()->getValue("syslog_tag", "ownCloud"), LOG_PID | LOG_CONS, LOG_USER); // Close at shutdown register_shutdown_function('closelog'); } /** * write a message in the log * @param string $app * @param string $message * @param int $level */ public static function write($app, $message, $level) { $syslog_level = self::$levels[$level]; syslog($syslog_level, '{'.$app.'} '.$message); } } private/Log/ErrorHandler.php 0000604 00000005567 15247130453 0012054 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Log; use OCP\ILogger; class ErrorHandler { /** @var ILogger */ private static $logger; /** * remove password in URLs * @param string $msg * @return string */ protected static function removePassword($msg) { return preg_replace('/\/\/(.*):(.*)@/', '//xxx:xxx@', $msg); } public static function register($debug=false) { $handler = new ErrorHandler(); if ($debug) { set_error_handler(array($handler, 'onAll'), E_ALL); if (\OC::$CLI) { set_exception_handler(array('OC_Template', 'printExceptionErrorPage')); } } else { set_error_handler(array($handler, 'onError')); } register_shutdown_function(array($handler, 'onShutdown')); set_exception_handler(array($handler, 'onException')); } public static function setLogger(ILogger $logger) { self::$logger = $logger; } //Fatal errors handler public static function onShutdown() { $error = error_get_last(); if($error && self::$logger) { //ob_end_clean(); $msg = $error['message'] . ' at ' . $error['file'] . '#' . $error['line']; self::$logger->critical(self::removePassword($msg), array('app' => 'PHP')); } } /** * Uncaught exception handler * * @param \Exception $exception */ public static function onException($exception) { $class = get_class($exception); $msg = $exception->getMessage(); $msg = "$class: $msg at " . $exception->getFile() . '#' . $exception->getLine(); self::$logger->critical(self::removePassword($msg), ['app' => 'PHP']); } //Recoverable errors handler public static function onError($number, $message, $file, $line) { if (error_reporting() === 0) { return; } $msg = $message . ' at ' . $file . '#' . $line; self::$logger->error(self::removePassword($msg), array('app' => 'PHP')); } //Recoverable handler which catch all errors, warnings and notices public static function onAll($number, $message, $file, $line) { $msg = $message . ' at ' . $file . '#' . $line; self::$logger->debug(self::removePassword($msg), array('app' => 'PHP')); } } private/Log/Errorlog.php 0000604 00000002760 15247130453 0011250 0 ustar 00 <?php /** * The MIT License (MIT) * * Copyright (c) 2014 Christian Kampka <christian@kampka.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace OC\Log; class Errorlog { /** * Init class data */ public static function init() { } /** * write a message in the log * @param string $app * @param string $message * @param int $level */ public static function write($app, $message, $level) { error_log('[owncloud]['.$app.']['.$level.'] '.$message); } } private/DateTimeZone.php 0000604 00000007025 15247130453 0011263 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OCP\IConfig; use OCP\IDateTimeZone; use OCP\ISession; class DateTimeZone implements IDateTimeZone { /** @var IConfig */ protected $config; /** @var ISession */ protected $session; /** * Constructor * * @param IConfig $config * @param ISession $session */ public function __construct(IConfig $config, ISession $session) { $this->config = $config; $this->session = $session; } /** * Get the timezone of the current user, based on his session information and config data * * @param bool|int $timestamp * @return \DateTimeZone */ public function getTimeZone($timestamp = false) { $timeZone = $this->config->getUserValue($this->session->get('user_id'), 'core', 'timezone', null); if ($timeZone === null) { if ($this->session->exists('timezone')) { return $this->guessTimeZoneFromOffset($this->session->get('timezone'), $timestamp); } $timeZone = $this->getDefaultTimeZone(); } try { return new \DateTimeZone($timeZone); } catch (\Exception $e) { \OCP\Util::writeLog('datetimezone', 'Failed to created DateTimeZone "' . $timeZone . "'", \OCP\Util::DEBUG); return new \DateTimeZone($this->getDefaultTimeZone()); } } /** * Guess the DateTimeZone for a given offset * * We first try to find a Etc/GMT* timezone, if that does not exist, * we try to find it manually, before falling back to UTC. * * @param mixed $offset * @param bool|int $timestamp * @return \DateTimeZone */ protected function guessTimeZoneFromOffset($offset, $timestamp) { try { // Note: the timeZone name is the inverse to the offset, // so a positive offset means negative timeZone // and the other way around. if ($offset > 0) { $timeZone = 'Etc/GMT-' . $offset; } else { $timeZone = 'Etc/GMT+' . abs($offset); } return new \DateTimeZone($timeZone); } catch (\Exception $e) { // If the offset has no Etc/GMT* timezone, // we try to guess one timezone that has the same offset foreach (\DateTimeZone::listIdentifiers() as $timeZone) { $dtz = new \DateTimeZone($timeZone); $dateTime = new \DateTime(); if ($timestamp !== false) { $dateTime->setTimestamp($timestamp); } $dtOffset = $dtz->getOffset($dateTime); if ($dtOffset == 3600 * $offset) { return $dtz; } } // No timezone found, fallback to UTC \OCP\Util::writeLog('datetimezone', 'Failed to find DateTimeZone for offset "' . $offset . "'", \OCP\Util::DEBUG); return new \DateTimeZone($this->getDefaultTimeZone()); } } /** * Get the default timezone of the server * * Falls back to UTC if it is not yet set. * * @return string */ protected function getDefaultTimeZone() { $serverTimeZone = date_default_timezone_get(); return $serverTimeZone ?: 'UTC'; } } private/Hooks/LegacyEmitter.php 0000604 00000002020 15247130453 0012542 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Hooks; abstract class LegacyEmitter extends BasicEmitter { protected function emit($scope, $method, array $arguments = array()) { \OC_Hook::emit($scope, $method, $arguments); parent::emit($scope, $method, $arguments); } } private/Hooks/BasicEmitter.php 0000604 00000001571 15247130453 0012371 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Hooks; abstract class BasicEmitter implements Emitter { use EmitterTrait; } private/Hooks/ForwardingEmitter.php 0000604 00000003350 15247130453 0013447 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Hooks; /** * Class ForwardingEmitter * * allows forwarding all listen calls to other emitters * * @package OC\Hooks */ abstract class ForwardingEmitter extends BasicEmitter { /** * @var \OC\Hooks\Emitter[] array */ private $forwardEmitters = array(); /** * @param string $scope * @param string $method * @param callable $callback */ public function listen($scope, $method, callable $callback) { parent::listen($scope, $method, $callback); foreach ($this->forwardEmitters as $emitter) { $emitter->listen($scope, $method, $callback); } } /** * @param \OC\Hooks\Emitter $emitter */ protected function forward(Emitter $emitter) { $this->forwardEmitters[] = $emitter; //forward all previously connected hooks foreach ($this->listeners as $key => $listeners) { list($scope, $method) = explode('::', $key, 2); foreach ($listeners as $listener) { $emitter->listen($scope, $method, $listener); } } } } private/Hooks/Emitter.php 0000604 00000002567 15247130453 0011435 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Hooks; /** * Class Emitter * * interface for all classes that are able to emit events * * @package OC\Hooks */ interface Emitter { /** * @param string $scope * @param string $method * @param callable $callback * @return void */ public function listen($scope, $method, callable $callback); /** * @param string $scope optional * @param string $method optional * @param callable $callback optional * @return void */ public function removeListener($scope = null, $method = null, callable $callback = null); } private/Hooks/EmitterTrait.php 0000604 00000005212 15247130453 0012427 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Hooks; trait EmitterTrait { /** * @var (callable[])[] $listeners */ protected $listeners = array(); /** * @param string $scope * @param string $method * @param callable $callback */ public function listen($scope, $method, callable $callback) { $eventName = $scope . '::' . $method; if (!isset($this->listeners[$eventName])) { $this->listeners[$eventName] = array(); } if (array_search($callback, $this->listeners[$eventName], true) === false) { $this->listeners[$eventName][] = $callback; } } /** * @param string $scope optional * @param string $method optional * @param callable $callback optional */ public function removeListener($scope = null, $method = null, callable $callback = null) { $names = array(); $allNames = array_keys($this->listeners); if ($scope and $method) { $name = $scope . '::' . $method; if (isset($this->listeners[$name])) { $names[] = $name; } } elseif ($scope) { foreach ($allNames as $name) { $parts = explode('::', $name, 2); if ($parts[0] == $scope) { $names[] = $name; } } } elseif ($method) { foreach ($allNames as $name) { $parts = explode('::', $name, 2); if ($parts[1] == $method) { $names[] = $name; } } } else { $names = $allNames; } foreach ($names as $name) { if ($callback) { $index = array_search($callback, $this->listeners[$name], true); if ($index !== false) { unset($this->listeners[$name][$index]); } } else { $this->listeners[$name] = array(); } } } /** * @param string $scope * @param string $method * @param array $arguments optional */ protected function emit($scope, $method, array $arguments = array()) { $eventName = $scope . '::' . $method; if (isset($this->listeners[$eventName])) { foreach ($this->listeners[$eventName] as $callback) { call_user_func_array($callback, $arguments); } } } } private/Hooks/PublicEmitter.php 0000604 00000002067 15247130453 0012567 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Hooks; class PublicEmitter extends BasicEmitter { /** * @param string $scope * @param string $method * @param array $arguments optional */ public function emit($scope, $method, array $arguments = array()) { parent::emit($scope, $method, $arguments); } } private/Http/Client/ClientService.php 0000604 00000002764 15247130453 0013634 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Http\Client; use GuzzleHttp\Client as GuzzleClient; use OCP\Http\Client\IClientService; use OCP\ICertificateManager; use OCP\IConfig; /** * Class ClientService * * @package OC\Http */ class ClientService implements IClientService { /** @var IConfig */ private $config; /** @var ICertificateManager */ private $certificateManager; /** * @param IConfig $config * @param ICertificateManager $certificateManager */ public function __construct(IConfig $config, ICertificateManager $certificateManager) { $this->config = $config; $this->certificateManager = $certificateManager; } /** * @return Client */ public function newClient() { return new Client($this->config, $this->certificateManager, new GuzzleClient()); } } private/Http/Client/Client.php 0000604 00000024752 15247130453 0012314 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Http\Client; use GuzzleHttp\Client as GuzzleClient; use OCP\Http\Client\IClient; use OCP\ICertificateManager; use OCP\IConfig; /** * Class Client * * @package OC\Http */ class Client implements IClient { /** @var GuzzleClient */ private $client; /** @var IConfig */ private $config; /** @var ICertificateManager */ private $certificateManager; private $configured = false; /** * @param IConfig $config * @param ICertificateManager $certificateManager * @param GuzzleClient $client */ public function __construct(IConfig $config, ICertificateManager $certificateManager, GuzzleClient $client) { $this->config = $config; $this->client = $client; $this->certificateManager = $certificateManager; } /** * Sets the default options to the client */ private function setDefaultOptions() { if ($this->configured) { return; } $this->configured = true; // Either use user bundle or the system bundle if nothing is specified if ($this->certificateManager->listCertificates() !== []) { $this->client->setDefaultOption('verify', $this->certificateManager->getAbsoluteBundlePath()); } else { // If the instance is not yet setup we need to use the static path as // $this->certificateManager->getAbsoluteBundlePath() tries to instantiiate // a view if ($this->config->getSystemValue('installed', false)) { $this->client->setDefaultOption('verify', $this->certificateManager->getAbsoluteBundlePath(null)); } else { $this->client->setDefaultOption('verify', \OC::$SERVERROOT . '/resources/config/ca-bundle.crt'); } } $this->client->setDefaultOption('headers/User-Agent', 'Nextcloud Server Crawler'); $proxyUri = $this->getProxyUri(); if ($proxyUri !== '') { $this->client->setDefaultOption('proxy', $proxyUri); } } /** * Get the proxy URI * * @return string */ private function getProxyUri() { $proxyHost = $this->config->getSystemValue('proxy', null); $proxyUserPwd = $this->config->getSystemValue('proxyuserpwd', null); $proxyUri = ''; if ($proxyUserPwd !== null) { $proxyUri .= $proxyUserPwd . '@'; } if ($proxyHost !== null) { $proxyUri .= $proxyHost; } return $proxyUri; } /** * Sends a GET request * * @param string $uri * @param array $options Array such as * 'query' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * 'timeout' => 5, * @return Response * @throws \Exception If the request could not get completed */ public function get($uri, array $options = []) { $this->setDefaultOptions(); $response = $this->client->get($uri, $options); $isStream = isset($options['stream']) && $options['stream']; return new Response($response, $isStream); } /** * Sends a HEAD request * * @param string $uri * @param array $options Array such as * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * 'timeout' => 5, * @return Response * @throws \Exception If the request could not get completed */ public function head($uri, $options = []) { $this->setDefaultOptions(); $response = $this->client->head($uri, $options); return new Response($response); } /** * Sends a POST request * * @param string $uri * @param array $options Array such as * 'body' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * 'timeout' => 5, * @return Response * @throws \Exception If the request could not get completed */ public function post($uri, array $options = []) { $this->setDefaultOptions(); $response = $this->client->post($uri, $options); return new Response($response); } /** * Sends a PUT request * * @param string $uri * @param array $options Array such as * 'body' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * 'timeout' => 5, * @return Response * @throws \Exception If the request could not get completed */ public function put($uri, array $options = []) { $this->setDefaultOptions(); $response = $this->client->put($uri, $options); return new Response($response); } /** * Sends a DELETE request * * @param string $uri * @param array $options Array such as * 'body' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * 'timeout' => 5, * @return Response * @throws \Exception If the request could not get completed */ public function delete($uri, array $options = []) { $this->setDefaultOptions(); $response = $this->client->delete($uri, $options); return new Response($response); } /** * Sends a options request * * @param string $uri * @param array $options Array such as * 'body' => [ * 'field' => 'abc', * 'other_field' => '123', * 'file_name' => fopen('/path/to/file', 'r'), * ], * 'headers' => [ * 'foo' => 'bar', * ], * 'cookies' => [' * 'foo' => 'bar', * ], * 'allow_redirects' => [ * 'max' => 10, // allow at most 10 redirects. * 'strict' => true, // use "strict" RFC compliant redirects. * 'referer' => true, // add a Referer header * 'protocols' => ['https'] // only allow https URLs * ], * 'save_to' => '/path/to/file', // save to a file or a stream * 'verify' => true, // bool or string to CA file * 'debug' => true, * 'timeout' => 5, * @return Response * @throws \Exception If the request could not get completed */ public function options($uri, array $options = []) { $this->setDefaultOptions(); $response = $this->client->options($uri, $options); return new Response($response); } } private/Http/Client/Response.php 0000604 00000003432 15247130453 0012664 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Http\Client; use OCP\Http\Client\IResponse; use GuzzleHttp\Message\Response as GuzzleResponse; /** * Class Response * * @package OC\Http */ class Response implements IResponse { /** @var GuzzleResponse */ private $response; /** * @var bool */ private $stream; /** * @param GuzzleResponse $response * @param bool $stream */ public function __construct(GuzzleResponse $response, $stream = false) { $this->response = $response; $this->stream = $stream; } /** * @return string|resource */ public function getBody() { return $this->stream ? $this->response->getBody()->detach(): $this->response->getBody()->getContents(); } /** * @return int */ public function getStatusCode() { return $this->response->getStatusCode(); } /** * @param $key * @return string */ public function getHeader($key) { return $this->response->getHeader($key); } /** * @return array */ public function getHeaders() { return $this->response->getHeaders(); } } private/Repair.php 0000604 00000015760 15247130453 0010162 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\App\AppStore\Bundles\BundleFetcher; use OC\Files\AppData\Factory; use OC\Repair\CleanTags; use OC\Repair\Collation; use OC\Repair\MoveUpdaterStepFile; use OC\Repair\NC11\CleanPreviews; use OC\Repair\NC11\FixMountStorages; use OC\Repair\NC11\MoveAvatars; use OC\Repair\NC12\InstallCoreBundle; use OC\Repair\NC12\UpdateLanguageCodes; use OC\Repair\NC12\RepairIdentityProofKeyFolders; use OC\Repair\OldGroupMembershipShares; use OC\Repair\Owncloud\DropAccountTermsTable; use OC\Repair\Owncloud\SaveAccountsTableData; use OC\Repair\RemoveRootShares; use OC\Repair\NC13\RepairInvalidPaths; use OC\Repair\SqliteAutoincrement; use OC\Repair\RepairMimeTypes; use OC\Repair\RepairInvalidShares; use OCP\AppFramework\QueryException; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\GenericEvent; class Repair implements IOutput{ /* @var IRepairStep[] */ private $repairSteps; /** @var EventDispatcher */ private $dispatcher; /** @var string */ private $currentStep; /** * Creates a new repair step runner * * @param IRepairStep[] $repairSteps array of RepairStep instances * @param EventDispatcher $dispatcher */ public function __construct($repairSteps = [], EventDispatcher $dispatcher = null) { $this->repairSteps = $repairSteps; $this->dispatcher = $dispatcher; } /** * Run a series of repair steps for common problems */ public function run() { if (count($this->repairSteps) === 0) { $this->emit('\OC\Repair', 'info', array('No repair steps available')); return; } // run each repair step foreach ($this->repairSteps as $step) { $this->currentStep = $step->getName(); $this->emit('\OC\Repair', 'step', [$this->currentStep]); $step->run($this); } } /** * Add repair step * * @param IRepairStep|string $repairStep repair step * @throws \Exception */ public function addStep($repairStep) { if (is_string($repairStep)) { try { $s = \OC::$server->query($repairStep); } catch (QueryException $e) { if (class_exists($repairStep)) { $s = new $repairStep(); } else { throw new \Exception("Repair step '$repairStep' is unknown"); } } if ($s instanceof IRepairStep) { $this->repairSteps[] = $s; } else { throw new \Exception("Repair step '$repairStep' is not of type \\OCP\\Migration\\IRepairStep"); } } else { $this->repairSteps[] = $repairStep; } } /** * Returns the default repair steps to be run on the * command line or after an upgrade. * * @return IRepairStep[] */ public static function getRepairSteps() { return [ new Collation(\OC::$server->getConfig(), \OC::$server->getLogger(), \OC::$server->getDatabaseConnection(), false), new RepairMimeTypes(\OC::$server->getConfig()), new CleanTags(\OC::$server->getDatabaseConnection(), \OC::$server->getUserManager()), new RepairInvalidShares(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()), new RemoveRootShares(\OC::$server->getDatabaseConnection(), \OC::$server->getUserManager(), \OC::$server->getLazyRootFolder()), new MoveUpdaterStepFile(\OC::$server->getConfig()), new MoveAvatars( \OC::$server->getJobList(), \OC::$server->getConfig() ), new CleanPreviews( \OC::$server->getJobList(), \OC::$server->getUserManager(), \OC::$server->getConfig() ), new FixMountStorages(\OC::$server->getDatabaseConnection()), new UpdateLanguageCodes(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig()), new InstallCoreBundle( \OC::$server->query(BundleFetcher::class), \OC::$server->getConfig(), \OC::$server->query(Installer::class) ), new RepairInvalidPaths(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig()), new RepairIdentityProofKeyFolders(\OC::$server->getConfig(), \OC::$server->query(Factory::class), \OC::$server->getRootFolder()), ]; } /** * Returns expensive repair steps to be run on the * command line with a special option. * * @return IRepairStep[] */ public static function getExpensiveRepairSteps() { return [ new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager()) ]; } /** * Returns the repair steps to be run before an * upgrade. * * @return IRepairStep[] */ public static function getBeforeUpgradeRepairSteps() { $connection = \OC::$server->getDatabaseConnection(); $config = \OC::$server->getConfig(); $steps = [ new Collation(\OC::$server->getConfig(), \OC::$server->getLogger(), $connection, true), new SqliteAutoincrement($connection), new SaveAccountsTableData($connection, $config), new DropAccountTermsTable($connection), ]; return $steps; } /** * @param string $scope * @param string $method * @param array $arguments */ public function emit($scope, $method, array $arguments = []) { if (!is_null($this->dispatcher)) { $this->dispatcher->dispatch("$scope::$method", new GenericEvent("$scope::$method", $arguments)); } } public function info($string) { // for now just emit as we did in the past $this->emit('\OC\Repair', 'info', array($string)); } /** * @param string $message */ public function warning($message) { // for now just emit as we did in the past $this->emit('\OC\Repair', 'warning', [$message]); } /** * @param int $max */ public function startProgress($max = 0) { // for now just emit as we did in the past $this->emit('\OC\Repair', 'startProgress', [$max, $this->currentStep]); } /** * @param int $step * @param string $description */ public function advance($step = 1, $description = '') { // for now just emit as we did in the past $this->emit('\OC\Repair', 'advance', [$step, $description]); } /** * @param int $max */ public function finishProgress() { // for now just emit as we did in the past $this->emit('\OC\Repair', 'finishProgress', []); } } private/RichObjectStrings/Validator.php 0000604 00000005724 15247130453 0014252 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\RichObjectStrings; use OCP\RichObjectStrings\Definitions; use OCP\RichObjectStrings\InvalidObjectExeption; use OCP\RichObjectStrings\IValidator; /** * Class Validator * * @package OCP\RichObjectStrings * @since 11.0.0 */ class Validator implements IValidator { /** @var Definitions */ protected $definitions; /** @var array[] */ protected $requiredParameters = []; /** * Constructor * * @param Definitions $definitions */ public function __construct(Definitions $definitions) { $this->definitions = $definitions; } /** * @param string $subject * @param array[] $parameters * @throws InvalidObjectExeption * @since 11.0.0 */ public function validate($subject, array $parameters) { $matches = []; $result = preg_match_all('/\{([a-z0-9]+)\}/i', $subject, $matches); if ($result === false) { throw new InvalidObjectExeption(); } if (!empty($matches[1])) { foreach ($matches[1] as $parameter) { if (!isset($parameters[$parameter])) { throw new InvalidObjectExeption('Parameter is undefined'); } else { $this->validateParameter($parameters[$parameter]); } } } } /** * @param array $parameter * @throws InvalidObjectExeption */ protected function validateParameter(array $parameter) { if (!isset($parameter['type'])) { throw new InvalidObjectExeption('Object type is undefined'); } $definition = $this->definitions->getDefinition($parameter['type']); $requiredParameters = $this->getRequiredParameters($parameter['type'], $definition); $missingKeys = array_diff($requiredParameters, array_keys($parameter)); if (!empty($missingKeys)) { throw new InvalidObjectExeption('Object is invalid'); } } /** * @param string $type * @param array $definition * @return string[] */ protected function getRequiredParameters($type, array $definition) { if (isset($this->requiredParameters[$type])) { return $this->requiredParameters[$type]; } $this->requiredParameters[$type] = []; foreach ($definition['parameters'] as $parameter => $data) { if ($data['required']) { $this->requiredParameters[$type][] = $parameter; } } return $this->requiredParameters[$type]; } } private/Security/Normalizer/IpAddress.php 0000604 00000005166 15247130453 0014546 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\Normalizer; /** * Class IpAddress is used for normalizing IPv4 and IPv6 addresses in security * relevant contexts in Nextcloud. * * @package OC\Security\Normalizer */ class IpAddress { /** @var string */ private $ip; /** * @param string $ip IP to normalized */ public function __construct($ip) { $this->ip = $ip; } /** * Return the given subnet for an IPv4 address and mask bits * * @param string $ip * @param int $maskBits * @return string */ private function getIPv4Subnet($ip, $maskBits = 32) { $binary = \inet_pton($ip); for ($i = 32; $i > $maskBits; $i -= 8) { $j = \intdiv($i, 8) - 1; $k = (int) \min(8, $i - $maskBits); $mask = (0xff - ((pow(2, $k)) - 1)); $int = \unpack('C', $binary[$j]); $binary[$j] = \pack('C', $int[1] & $mask); } return \inet_ntop($binary).'/'.$maskBits; } /** * Return the given subnet for an IPv6 address and mask bits * * @param string $ip * @param int $maskBits * @return string */ private function getIPv6Subnet($ip, $maskBits = 48) { $binary = \inet_pton($ip); for ($i = 128; $i > $maskBits; $i -= 8) { $j = \intdiv($i, 8) - 1; $k = (int) \min(8, $i - $maskBits); $mask = (0xff - ((pow(2, $k)) - 1)); $int = \unpack('C', $binary[$j]); $binary[$j] = \pack('C', $int[1] & $mask); } return \inet_ntop($binary).'/'.$maskBits; } /** * Gets either the /32 (IPv4) or the /128 (IPv6) subnet of an IP address * * @return string */ public function getSubnet() { if (\preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $this->ip)) { return $this->getIPv4Subnet( $this->ip, 32 ); } return $this->getIPv6Subnet( $this->ip, 128 ); } /** * Returns the specified IP address * * @return string */ public function __toString() { return $this->ip; } } private/Security/CredentialsManager.php 0000604 00000006324 15247130453 0014273 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security; use OCP\Security\ICrypto; use OCP\IDBConnection; use OCP\Security\ICredentialsManager; /** * Store and retrieve credentials for external services * * @package OC\Security */ class CredentialsManager implements ICredentialsManager { const DB_TABLE = 'credentials'; /** @var ICrypto */ protected $crypto; /** @var IDBConnection */ protected $dbConnection; /** * @param ICrypto $crypto * @param IDBConnection $dbConnection */ public function __construct(ICrypto $crypto, IDBConnection $dbConnection) { $this->crypto = $crypto; $this->dbConnection = $dbConnection; } /** * Store a set of credentials * * @param string|null $userId Null for system-wide credentials * @param string $identifier * @param mixed $credentials */ public function store($userId, $identifier, $credentials) { $value = $this->crypto->encrypt(json_encode($credentials)); $this->dbConnection->setValues(self::DB_TABLE, [ 'user' => $userId, 'identifier' => $identifier, ], [ 'credentials' => $value, ]); } /** * Retrieve a set of credentials * * @param string|null $userId Null for system-wide credentials * @param string $identifier * @return mixed */ public function retrieve($userId, $identifier) { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('credentials') ->from(self::DB_TABLE) ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId))) ->andWhere($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier))) ; $result = $qb->execute()->fetch(); if (!$result) { return null; } $value = $result['credentials']; return json_decode($this->crypto->decrypt($value), true); } /** * Delete a set of credentials * * @param string|null $userId Null for system-wide credentials * @param string $identifier * @return int rows removed */ public function delete($userId, $identifier) { $qb = $this->dbConnection->getQueryBuilder(); $qb->delete(self::DB_TABLE) ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId))) ->andWhere($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier))) ; return $qb->execute(); } /** * Erase all credentials stored for a user * * @param string $userId * @return int rows removed */ public function erase($userId) { $qb = $this->dbConnection->getQueryBuilder(); $qb->delete(self::DB_TABLE) ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId))) ; return $qb->execute(); } } private/Security/Certificate.php 0000604 00000006002 15247130453 0012756 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security; use OCP\ICertificate; class Certificate implements ICertificate { protected $name; protected $commonName; protected $organization; protected $serial; protected $issueDate; protected $expireDate; protected $issuerName; protected $issuerOrganization; /** * @param string $data base64 encoded certificate * @param string $name * @throws \Exception If the certificate could not get parsed */ public function __construct($data, $name) { $this->name = $name; $gmt = new \DateTimeZone('GMT'); // If string starts with "file://" ignore the certificate $query = 'file://'; if(strtolower(substr($data, 0, strlen($query))) === $query) { throw new \Exception('Certificate could not get parsed.'); } $info = openssl_x509_parse($data); if(!is_array($info)) { throw new \Exception('Certificate could not get parsed.'); } $this->commonName = isset($info['subject']['CN']) ? $info['subject']['CN'] : null; $this->organization = isset($info['subject']['O']) ? $info['subject']['O'] : null; $this->issueDate = new \DateTime('@' . $info['validFrom_time_t'], $gmt); $this->expireDate = new \DateTime('@' . $info['validTo_time_t'], $gmt); $this->issuerName = isset($info['issuer']['CN']) ? $info['issuer']['CN'] : null; $this->issuerOrganization = isset($info['issuer']['O']) ? $info['issuer']['O'] : null; } /** * @return string */ public function getName() { return $this->name; } /** * @return string|null */ public function getCommonName() { return $this->commonName; } /** * @return string */ public function getOrganization() { return $this->organization; } /** * @return \DateTime */ public function getIssueDate() { return $this->issueDate; } /** * @return \DateTime */ public function getExpireDate() { return $this->expireDate; } /** * @return bool */ public function isExpired() { $now = new \DateTime(); return $this->issueDate > $now or $now > $this->expireDate; } /** * @return string|null */ public function getIssuerName() { return $this->issuerName; } /** * @return string|null */ public function getIssuerOrganization() { return $this->issuerOrganization; } } private/Security/TrustedDomainHelper.php 0000604 00000005376 15247130453 0014473 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Johannes Ernst <jernst@indiecomputing.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security; use OC\AppFramework\Http\Request; use OCP\IConfig; /** * Class TrustedDomain * * @package OC\Security */ class TrustedDomainHelper { /** @var IConfig */ private $config; /** * @param IConfig $config */ function __construct(IConfig $config) { $this->config = $config; } /** * Strips a potential port from a domain (in format domain:port) * @param string $host * @return string $host without appended port */ private function getDomainWithoutPort($host) { $pos = strrpos($host, ':'); if ($pos !== false) { $port = substr($host, $pos + 1); if (is_numeric($port)) { $host = substr($host, 0, $pos); } } return $host; } /** * Checks whether a domain is considered as trusted from the list * of trusted domains. If no trusted domains have been configured, returns * true. * This is used to prevent Host Header Poisoning. * @param string $domainWithPort * @return bool true if the given domain is trusted or if no trusted domains * have been configured */ public function isTrustedDomain($domainWithPort) { $domain = $this->getDomainWithoutPort($domainWithPort); // Read trusted domains from config $trustedList = $this->config->getSystemValue('trusted_domains', []); if (!is_array($trustedList)) { return false; } // Always allow access from localhost if (preg_match(Request::REGEX_LOCALHOST, $domain) === 1) { return true; } // Reject misformed domains in any case if (strpos($domain,'-') === 0 || strpos($domain,'..') !== false) { return false; } // Match, allowing for * wildcards foreach ($trustedList as $trusted) { if (gettype($trusted) !== 'string') { break; } $regex = '/^' . join('[-\.a-zA-Z0-9]*', array_map(function($v) { return preg_quote($v, '/'); }, explode('*', $trusted))) . '$/'; if (preg_match($regex, $domain) || preg_match($regex, $domainWithPort)) { return true; } } return false; } } private/Security/CSRF/CsrfToken.php 0000604 00000004214 15247130453 0013172 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security\CSRF; /** * Class CsrfToken represents the stored or provided CSRF token. To mitigate * BREACH alike vulnerabilities the token is returned in an encrypted value as * well in an unencrypted value. For display measures to the user always the * unencrypted one should be chosen. * * @package OC\Security\CSRF */ class CsrfToken { /** @var string */ private $value; /** @var string */ private $encryptedValue = ''; /** * @param string $value Value of the token. Can be encrypted or not encrypted. */ public function __construct($value) { $this->value = $value; } /** * Encrypted value of the token. This is used to mitigate BREACH alike * vulnerabilities. For display measures do use this functionality. * * @return string */ public function getEncryptedValue() { if($this->encryptedValue === '') { $sharedSecret = random_bytes(strlen($this->value)); $this->encryptedValue = base64_encode($this->value ^ $sharedSecret) . ':' . base64_encode($sharedSecret); } return $this->encryptedValue; } /** * The unencrypted value of the token. Used for decrypting an already * encrypted token. * * @return int */ public function getDecryptedValue() { $token = explode(':', $this->value); if (count($token) !== 2) { return ''; } $obfuscatedToken = $token[0]; $secret = $token[1]; return base64_decode($obfuscatedToken) ^ base64_decode($secret); } } private/Security/CSRF/TokenStorage/SessionStorage.php 0000604 00000003711 15247130453 0016652 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security\CSRF\TokenStorage; use OCP\ISession; /** * Class SessionStorage provides the session storage * * @package OC\Security\CSRF\TokenStorage */ class SessionStorage { /** @var ISession */ private $session; /** * @param ISession $session */ public function __construct(ISession $session) { $this->session = $session; } /** * @param ISession $session */ public function setSession(ISession $session) { $this->session = $session; } /** * Returns the current token or throws an exception if none is found. * * @return string * @throws \Exception */ public function getToken() { $token = $this->session->get('requesttoken'); if(empty($token)) { throw new \Exception('Session does not contain a requesttoken'); } return $token; } /** * Set the valid current token to $value. * * @param string $value */ public function setToken($value) { $this->session->set('requesttoken', $value); } /** * Removes the current token. */ public function removeToken() { $this->session->remove('requesttoken'); } /** * Whether the storage has a storage. * * @return bool */ public function hasToken() { return $this->session->exists('requesttoken'); } } private/Security/CSRF/CsrfTokenGenerator.php 0000604 00000002556 15247130453 0015050 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security\CSRF; use OCP\Security\ISecureRandom; /** * Class CsrfTokenGenerator is used to generate a cryptographically secure * pseudo-random number for the token. * * @package OC\Security\CSRF */ class CsrfTokenGenerator { /** @var ISecureRandom */ private $random; /** * @param ISecureRandom $random */ public function __construct(ISecureRandom $random) { $this->random = $random; } /** * Generate a new CSRF token. * * @param int $length Length of the token in characters. * @return string */ public function generateToken($length = 32) { return $this->random->generate($length); } } private/Security/CSRF/CsrfTokenManager.php 0000604 00000005231 15247130453 0014465 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security\CSRF; use OC\Security\CSRF\TokenStorage\SessionStorage; /** * Class CsrfTokenManager is the manager for all CSRF token related activities. * * @package OC\Security\CSRF */ class CsrfTokenManager { /** @var CsrfTokenGenerator */ private $tokenGenerator; /** @var SessionStorage */ private $sessionStorage; /** @var CsrfToken|null */ private $csrfToken = null; /** * @param CsrfTokenGenerator $tokenGenerator * @param SessionStorage $storageInterface */ public function __construct(CsrfTokenGenerator $tokenGenerator, SessionStorage $storageInterface) { $this->tokenGenerator = $tokenGenerator; $this->sessionStorage = $storageInterface; } /** * Returns the current CSRF token, if none set it will create a new one. * * @return CsrfToken */ public function getToken() { if(!is_null($this->csrfToken)) { return $this->csrfToken; } if($this->sessionStorage->hasToken()) { $value = $this->sessionStorage->getToken(); } else { $value = $this->tokenGenerator->generateToken(); $this->sessionStorage->setToken($value); } $this->csrfToken = new CsrfToken($value); return $this->csrfToken; } /** * Invalidates any current token and sets a new one. * * @return CsrfToken */ public function refreshToken() { $value = $this->tokenGenerator->generateToken(); $this->sessionStorage->setToken($value); $this->csrfToken = new CsrfToken($value); return $this->csrfToken; } /** * Remove the current token from the storage. */ public function removeToken() { $this->csrfToken = null; $this->sessionStorage->removeToken(); } /** * Verifies whether the provided token is valid. * * @param CsrfToken $token * @return bool */ public function isTokenValid(CsrfToken $token) { if(!$this->sessionStorage->hasToken()) { return false; } return hash_equals( $this->sessionStorage->getToken(), $token->getDecryptedValue() ); } } private/Security/Crypto.php 0000604 00000010032 15247130453 0012012 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security; use phpseclib\Crypt\AES; use phpseclib\Crypt\Hash; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; use OCP\IConfig; /** * Class Crypto provides a high-level encryption layer using AES-CBC. If no key has been provided * it will use the secret defined in config.php as key. Additionally the message will be HMAC'd. * * Usage: * $encryptWithDefaultPassword = \OC::$server->getCrypto()->encrypt('EncryptedText'); * $encryptWithCustompassword = \OC::$server->getCrypto()->encrypt('EncryptedText', 'password'); * * @package OC\Security */ class Crypto implements ICrypto { /** @var AES $cipher */ private $cipher; /** @var int */ private $ivLength = 16; /** @var IConfig */ private $config; /** @var ISecureRandom */ private $random; /** * @param IConfig $config * @param ISecureRandom $random */ function __construct(IConfig $config, ISecureRandom $random) { $this->cipher = new AES(); $this->config = $config; $this->random = $random; } /** * @param string $message The message to authenticate * @param string $password Password to use (defaults to `secret` in config.php) * @return string Calculated HMAC */ public function calculateHMAC($message, $password = '') { if($password === '') { $password = $this->config->getSystemValue('secret'); } // Append an "a" behind the password and hash it to prevent reusing the same password as for encryption $password = hash('sha512', $password . 'a'); $hash = new Hash('sha512'); $hash->setKey($password); return $hash->hash($message); } /** * Encrypts a value and adds an HMAC (Encrypt-Then-MAC) * @param string $plaintext * @param string $password Password to encrypt, if not specified the secret from config.php will be taken * @return string Authenticated ciphertext */ public function encrypt($plaintext, $password = '') { if($password === '') { $password = $this->config->getSystemValue('secret'); } $this->cipher->setPassword($password); $iv = $this->random->generate($this->ivLength); $this->cipher->setIV($iv); $ciphertext = bin2hex($this->cipher->encrypt($plaintext)); $hmac = bin2hex($this->calculateHMAC($ciphertext.$iv, $password)); return $ciphertext.'|'.$iv.'|'.$hmac; } /** * Decrypts a value and verifies the HMAC (Encrypt-Then-Mac) * @param string $authenticatedCiphertext * @param string $password Password to encrypt, if not specified the secret from config.php will be taken * @return string plaintext * @throws \Exception If the HMAC does not match */ public function decrypt($authenticatedCiphertext, $password = '') { if($password === '') { $password = $this->config->getSystemValue('secret'); } $this->cipher->setPassword($password); $parts = explode('|', $authenticatedCiphertext); if(sizeof($parts) !== 3) { throw new \Exception('Authenticated ciphertext could not be decoded.'); } $ciphertext = hex2bin($parts[0]); $iv = $parts[1]; $hmac = hex2bin($parts[2]); $this->cipher->setIV($iv); if(!hash_equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) { throw new \Exception('HMAC does not match.'); } return $this->cipher->decrypt($ciphertext); } } private/Security/CSP/ContentSecurityPolicyNonceManager.php 0000604 00000003765 15247130453 0017776 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\CSP; use OC\AppFramework\Http\Request; use OC\Security\CSRF\CsrfTokenManager; use OCP\IRequest; /** * @package OC\Security\CSP */ class ContentSecurityPolicyNonceManager { /** @var CsrfTokenManager */ private $csrfTokenManager; /** @var IRequest */ private $request; /** @var string */ private $nonce = ''; /** * @param CsrfTokenManager $csrfTokenManager * @param IRequest $request */ public function __construct(CsrfTokenManager $csrfTokenManager, IRequest $request) { $this->csrfTokenManager = $csrfTokenManager; $this->request = $request; } /** * Returns the current CSP nounce * * @return string */ public function getNonce() { if($this->nonce === '') { $this->nonce = base64_encode($this->csrfTokenManager->getToken()->getEncryptedValue()); } return $this->nonce; } /** * Check if the browser supports CSP v3 * * @return bool */ public function browserSupportsCspV3() { $browserWhitelist = [ Request::USER_AGENT_CHROME, // Firefox 45+ '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/(4[5-9]|[5-9][0-9])\.[0-9.]+$/', ]; if($this->request->isUserAgent($browserWhitelist)) { return true; } return false; } } private/Security/CSP/ContentSecurityPolicyManager.php 0000604 00000004550 15247130453 0017004 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security\CSP; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\EmptyContentSecurityPolicy; use OCP\Security\IContentSecurityPolicyManager; class ContentSecurityPolicyManager implements IContentSecurityPolicyManager { /** @var ContentSecurityPolicy[] */ private $policies = []; /** {@inheritdoc} */ public function addDefaultPolicy(EmptyContentSecurityPolicy $policy) { $this->policies[] = $policy; } /** * Get the configured default policy. This is not in the public namespace * as it is only supposed to be used by core itself. * * @return ContentSecurityPolicy */ public function getDefaultPolicy() { $defaultPolicy = new \OC\Security\CSP\ContentSecurityPolicy(); foreach($this->policies as $policy) { $defaultPolicy = $this->mergePolicies($defaultPolicy, $policy); } return $defaultPolicy; } /** * Merges the first given policy with the second one * * @param ContentSecurityPolicy $defaultPolicy * @param EmptyContentSecurityPolicy $originalPolicy * @return ContentSecurityPolicy */ public function mergePolicies(ContentSecurityPolicy $defaultPolicy, EmptyContentSecurityPolicy $originalPolicy) { foreach((object)(array)$originalPolicy as $name => $value) { $setter = 'set'.ucfirst($name); if(is_array($value)) { $getter = 'get'.ucfirst($name); $currentValues = is_array($defaultPolicy->$getter()) ? $defaultPolicy->$getter() : []; $defaultPolicy->$setter(array_values(array_unique(array_merge($currentValues, $value)))); } elseif (is_bool($value)) { $defaultPolicy->$setter($value); } } return $defaultPolicy; } } private/Security/CSP/ContentSecurityPolicy.php 0000604 00000010626 15247130453 0015512 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security\CSP; /** * Class ContentSecurityPolicy extends the public class and adds getter and setters. * This is necessary since we don't want to expose the setters and getters to the * public API. * * @package OC\Security\CSP */ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy { /** * @return boolean */ public function isInlineScriptAllowed() { return $this->inlineScriptAllowed; } /** * @param boolean $inlineScriptAllowed */ public function setInlineScriptAllowed($inlineScriptAllowed) { $this->inlineScriptAllowed = $inlineScriptAllowed; } /** * @return boolean */ public function isEvalScriptAllowed() { return $this->evalScriptAllowed; } /** * @param boolean $evalScriptAllowed */ public function setEvalScriptAllowed($evalScriptAllowed) { $this->evalScriptAllowed = $evalScriptAllowed; } /** * @return array */ public function getAllowedScriptDomains() { return $this->allowedScriptDomains; } /** * @param array $allowedScriptDomains */ public function setAllowedScriptDomains($allowedScriptDomains) { $this->allowedScriptDomains = $allowedScriptDomains; } /** * @return boolean */ public function isInlineStyleAllowed() { return $this->inlineStyleAllowed; } /** * @param boolean $inlineStyleAllowed */ public function setInlineStyleAllowed($inlineStyleAllowed) { $this->inlineStyleAllowed = $inlineStyleAllowed; } /** * @return array */ public function getAllowedStyleDomains() { return $this->allowedStyleDomains; } /** * @param array $allowedStyleDomains */ public function setAllowedStyleDomains($allowedStyleDomains) { $this->allowedStyleDomains = $allowedStyleDomains; } /** * @return array */ public function getAllowedImageDomains() { return $this->allowedImageDomains; } /** * @param array $allowedImageDomains */ public function setAllowedImageDomains($allowedImageDomains) { $this->allowedImageDomains = $allowedImageDomains; } /** * @return array */ public function getAllowedConnectDomains() { return $this->allowedConnectDomains; } /** * @param array $allowedConnectDomains */ public function setAllowedConnectDomains($allowedConnectDomains) { $this->allowedConnectDomains = $allowedConnectDomains; } /** * @return array */ public function getAllowedMediaDomains() { return $this->allowedMediaDomains; } /** * @param array $allowedMediaDomains */ public function setAllowedMediaDomains($allowedMediaDomains) { $this->allowedMediaDomains = $allowedMediaDomains; } /** * @return array */ public function getAllowedObjectDomains() { return $this->allowedObjectDomains; } /** * @param array $allowedObjectDomains */ public function setAllowedObjectDomains($allowedObjectDomains) { $this->allowedObjectDomains = $allowedObjectDomains; } /** * @return array */ public function getAllowedFrameDomains() { return $this->allowedFrameDomains; } /** * @param array $allowedFrameDomains */ public function setAllowedFrameDomains($allowedFrameDomains) { $this->allowedFrameDomains = $allowedFrameDomains; } /** * @return array */ public function getAllowedFontDomains() { return $this->allowedFontDomains; } /** * @param array $allowedFontDomains */ public function setAllowedFontDomains($allowedFontDomains) { $this->allowedFontDomains = $allowedFontDomains; } /** * @return array */ public function getAllowedChildSrcDomains() { return $this->allowedChildSrcDomains; } /** * @param array $allowedChildSrcDomains */ public function setAllowedChildSrcDomains($allowedChildSrcDomains) { $this->allowedChildSrcDomains = $allowedChildSrcDomains; } } private/Security/CertificateManager.php 0000604 00000017324 15247130453 0014262 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security; use OC\Files\Filesystem; use OCP\ICertificateManager; use OCP\IConfig; use OCP\ILogger; use OCP\Security\ISecureRandom; /** * Manage trusted certificates for users */ class CertificateManager implements ICertificateManager { /** * @var string */ protected $uid; /** * @var \OC\Files\View */ protected $view; /** * @var IConfig */ protected $config; /** * @var ILogger */ protected $logger; /** @var ISecureRandom */ protected $random; /** * @param string $uid * @param \OC\Files\View $view relative to data/ * @param IConfig $config * @param ILogger $logger * @param ISecureRandom $random */ public function __construct($uid, \OC\Files\View $view, IConfig $config, ILogger $logger, ISecureRandom $random) { $this->uid = $uid; $this->view = $view; $this->config = $config; $this->logger = $logger; $this->random = $random; } /** * Returns all certificates trusted by the user * * @return \OCP\ICertificate[] */ public function listCertificates() { if (!$this->config->getSystemValue('installed', false)) { return array(); } $path = $this->getPathToCertificates() . 'uploads/'; if (!$this->view->is_dir($path)) { return array(); } $result = array(); $handle = $this->view->opendir($path); if (!is_resource($handle)) { return array(); } while (false !== ($file = readdir($handle))) { if ($file != '.' && $file != '..') { try { $result[] = new Certificate($this->view->file_get_contents($path . $file), $file); } catch (\Exception $e) { } } } closedir($handle); return $result; } /** * create the certificate bundle of all trusted certificated */ public function createCertificateBundle() { $path = $this->getPathToCertificates(); $certs = $this->listCertificates(); if (!$this->view->file_exists($path)) { $this->view->mkdir($path); } $defaultCertificates = file_get_contents(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'); if (strlen($defaultCertificates) < 1024) { // sanity check to verify that we have some content for our bundle // log as exception so we have a stacktrace $this->logger->logException(new \Exception('Shipped ca-bundle is empty, refusing to create certificate bundle')); return; } $certPath = $path . 'rootcerts.crt'; $tmpPath = $certPath . '.tmp' . $this->random->generate(10, ISecureRandom::CHAR_DIGITS); $fhCerts = $this->view->fopen($tmpPath, 'w'); // Write user certificates foreach ($certs as $cert) { $file = $path . '/uploads/' . $cert->getName(); $data = $this->view->file_get_contents($file); if (strpos($data, 'BEGIN CERTIFICATE')) { fwrite($fhCerts, $data); fwrite($fhCerts, "\r\n"); } } // Append the default certificates fwrite($fhCerts, $defaultCertificates); // Append the system certificate bundle $systemBundle = $this->getCertificateBundle(null); if ($systemBundle !== $certPath && $this->view->file_exists($systemBundle)) { $systemCertificates = $this->view->file_get_contents($systemBundle); fwrite($fhCerts, $systemCertificates); } fclose($fhCerts); $this->view->rename($tmpPath, $certPath); } /** * Save the certificate and re-generate the certificate bundle * * @param string $certificate the certificate data * @param string $name the filename for the certificate * @return \OCP\ICertificate * @throws \Exception If the certificate could not get added */ public function addCertificate($certificate, $name) { if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) { throw new \Exception('Filename is not valid'); } $dir = $this->getPathToCertificates() . 'uploads/'; if (!$this->view->file_exists($dir)) { $this->view->mkdir($dir); } try { $file = $dir . $name; $certificateObject = new Certificate($certificate, $name); $this->view->file_put_contents($file, $certificate); $this->createCertificateBundle(); return $certificateObject; } catch (\Exception $e) { throw $e; } } /** * Remove the certificate and re-generate the certificate bundle * * @param string $name * @return bool */ public function removeCertificate($name) { if (!Filesystem::isValidPath($name)) { return false; } $path = $this->getPathToCertificates() . 'uploads/'; if ($this->view->file_exists($path . $name)) { $this->view->unlink($path . $name); $this->createCertificateBundle(); } return true; } /** * Get the path to the certificate bundle for this user * * @param string $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle * @return string */ public function getCertificateBundle($uid = '') { if ($uid === '') { $uid = $this->uid; } return $this->getPathToCertificates($uid) . 'rootcerts.crt'; } /** * Get the full local path to the certificate bundle for this user * * @param string $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle * @return string */ public function getAbsoluteBundlePath($uid = '') { if ($uid === '') { $uid = $this->uid; } if ($this->needsRebundling($uid)) { if (is_null($uid)) { $manager = new CertificateManager(null, $this->view, $this->config, $this->logger, $this->random); $manager->createCertificateBundle(); } else { $this->createCertificateBundle(); } } return $this->view->getLocalFile($this->getCertificateBundle($uid)); } /** * @param string $uid (optional) user to get the certificate path for, use `null` to get the system path * @return string */ private function getPathToCertificates($uid = '') { if ($uid === '') { $uid = $this->uid; } $path = is_null($uid) ? '/files_external/' : '/' . $uid . '/files_external/'; return $path; } /** * Check if we need to re-bundle the certificates because one of the sources has updated * * @param string $uid (optional) user to get the certificate path for, use `null` to get the system path * @return bool */ private function needsRebundling($uid = '') { if ($uid === '') { $uid = $this->uid; } $sourceMTimes = [$this->getFilemtimeOfCaBundle()]; $targetBundle = $this->getCertificateBundle($uid); if (!$this->view->file_exists($targetBundle)) { return true; } if (!is_null($uid)) { // also depend on the system bundle $sourceMTimes[] = $this->view->filemtime($this->getCertificateBundle(null)); } $sourceMTime = array_reduce($sourceMTimes, function ($max, $mtime) { return max($max, $mtime); }, 0); return $sourceMTime > $this->view->filemtime($targetBundle); } /** * get mtime of ca-bundle shipped by Nextcloud * * @return int */ protected function getFilemtimeOfCaBundle() { return filemtime(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'); } } private/Security/SecureRandom.php 0000604 00000005423 15247130453 0013131 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security; use OCP\Security\ISecureRandom; /** * Class SecureRandom provides a wrapper around the random_int function to generate * secure random strings. For PHP 7 the native CSPRNG is used, older versions do * use a fallback. * * Usage: * \OC::$server->getSecureRandom()->generate(10); * @package OC\Security */ class SecureRandom implements ISecureRandom { /** * Convenience method to get a low strength random number generator. * * Low Strength should be used anywhere that random strings are needed * in a non-cryptographical setting. They are not strong enough to be * used as keys or salts. They are however useful for one-time use tokens. * * @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int() * @return $this */ public function getLowStrengthGenerator() { return $this; } /** * Convenience method to get a medium strength random number generator. * * Medium Strength should be used for most needs of a cryptographic nature. * They are strong enough to be used as keys and salts. However, they do * take some time and resources to generate, so they should not be over-used * * @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int() * @return $this */ public function getMediumStrengthGenerator() { return $this; } /** * Generate a random string of specified length. * @param int $length The length of the generated string * @param string $characters An optional list of characters to use if no character list is * specified all valid base64 characters are used. * @return string */ public function generate($length, $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') { $maxCharIndex = strlen($characters) - 1; $randomString = ''; while($length > 0) { $randomNumber = \random_int(0, $maxCharIndex); $randomString .= $characters[$randomNumber]; $length--; } return $randomString; } } private/Security/Bruteforce/Throttler.php 0000604 00000014555 15247130453 0014637 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @author Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\Bruteforce; use OC\Security\Normalizer\IpAddress; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\ILogger; /** * Class Throttler implements the bruteforce protection for security actions in * Nextcloud. * * It is working by logging invalid login attempts to the database and slowing * down all login attempts from the same subnet. The max delay is 30 seconds and * the starting delay are 200 milliseconds. (after the first failed login) * * This is based on Paragonie's AirBrake for Airship CMS. You can find the original * code at https://github.com/paragonie/airship/blob/7e5bad7e3c0fbbf324c11f963fd1f80e59762606/src/Engine/Security/AirBrake.php * * @package OC\Security\Bruteforce */ class Throttler { const LOGIN_ACTION = 'login'; /** @var IDBConnection */ private $db; /** @var ITimeFactory */ private $timeFactory; /** @var ILogger */ private $logger; /** @var IConfig */ private $config; /** * @param IDBConnection $db * @param ITimeFactory $timeFactory * @param ILogger $logger * @param IConfig $config */ public function __construct(IDBConnection $db, ITimeFactory $timeFactory, ILogger $logger, IConfig $config) { $this->db = $db; $this->timeFactory = $timeFactory; $this->logger = $logger; $this->config = $config; } /** * Convert a number of seconds into the appropriate DateInterval * * @param int $expire * @return \DateInterval */ private function getCutoff($expire) { $d1 = new \DateTime(); $d2 = clone $d1; $d2->sub(new \DateInterval('PT' . $expire . 'S')); return $d2->diff($d1); } /** * Register a failed attempt to bruteforce a security control * * @param string $action * @param string $ip * @param array $metadata Optional metadata logged to the database */ public function registerAttempt($action, $ip, array $metadata = []) { // No need to log if the bruteforce protection is disabled if($this->config->getSystemValue('auth.bruteforce.protection.enabled', true) === false) { return; } $ipAddress = new IpAddress($ip); $values = [ 'action' => $action, 'occurred' => $this->timeFactory->getTime(), 'ip' => (string)$ipAddress, 'subnet' => $ipAddress->getSubnet(), 'metadata' => json_encode($metadata), ]; $this->logger->notice( sprintf( 'Bruteforce attempt from "%s" detected for action "%s".', $ip, $action ), [ 'app' => 'core', ] ); $qb = $this->db->getQueryBuilder(); $qb->insert('bruteforce_attempts'); foreach($values as $column => $value) { $qb->setValue($column, $qb->createNamedParameter($value)); } $qb->execute(); } /** * Check if the IP is whitelisted * * @param string $ip * @return bool */ private function isIPWhitelisted($ip) { if($this->config->getSystemValue('auth.bruteforce.protection.enabled', true) === false) { return true; } $keys = $this->config->getAppKeys('bruteForce'); $keys = array_filter($keys, function($key) { $regex = '/^whitelist_/S'; return preg_match($regex, $key) === 1; }); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { $type = 4; } else if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $type = 6; } else { return false; } $ip = inet_pton($ip); foreach ($keys as $key) { $cidr = $this->config->getAppValue('bruteForce', $key, null); $cx = explode('/', $cidr); $addr = $cx[0]; $mask = (int)$cx[1]; // Do not compare ipv4 to ipv6 if (($type === 4 && !filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) || ($type === 6 && !filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))) { continue; } $addr = inet_pton($addr); $valid = true; for($i = 0; $i < $mask; $i++) { $part = ord($addr[(int)($i/8)]); $orig = ord($ip[(int)($i/8)]); $part = $part & (15 << (1 - ($i % 2))); $orig = $orig & (15 << (1 - ($i % 2))); if ($part !== $orig) { $valid = false; break; } } if ($valid === true) { return true; } } return false; } /** * Get the throttling delay (in milliseconds) * * @param string $ip * @param string $action optionally filter by action * @return int */ public function getDelay($ip, $action = '') { $ipAddress = new IpAddress($ip); if ($this->isIPWhitelisted((string)$ipAddress)) { return 0; } $cutoffTime = (new \DateTime()) ->sub($this->getCutoff(43200)) ->getTimestamp(); $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from('bruteforce_attempts') ->where($qb->expr()->gt('occurred', $qb->createNamedParameter($cutoffTime))) ->andWhere($qb->expr()->eq('subnet', $qb->createNamedParameter($ipAddress->getSubnet()))); if ($action !== '') { $qb->andWhere($qb->expr()->eq('action', $qb->createNamedParameter($action))); } $attempts = count($qb->execute()->fetchAll()); if ($attempts === 0) { return 0; } $maxDelay = 30; $firstDelay = 0.1; if ($attempts > (8 * PHP_INT_SIZE - 1)) { // Don't ever overflow. Just assume the maxDelay time:s $firstDelay = $maxDelay; } else { $firstDelay *= pow(2, $attempts); if ($firstDelay > $maxDelay) { $firstDelay = $maxDelay; } } return (int) \ceil($firstDelay * 1000); } /** * Will sleep for the defined amount of time * * @param string $ip * @param string $action optionally filter by action * @return int the time spent sleeping */ public function sleepDelay($ip, $action = '') { $delay = $this->getDelay($ip, $action); usleep($delay * 1000); return $delay; } } private/Security/Hasher.php 0000604 00000012036 15247130453 0011752 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Security; use OCP\IConfig; use OCP\Security\IHasher; /** * Class Hasher provides some basic hashing functions. Furthermore, it supports legacy hashes * used by previous versions of ownCloud and helps migrating those hashes to newer ones. * * The hashes generated by this class are prefixed (version|hash) with a version parameter to allow possible * updates in the future. * Possible versions: * - 1 (Initial version) * * Usage: * // Hashing a message * $hash = \OC::$server->getHasher()->hash('MessageToHash'); * // Verifying a message - $newHash will contain the newly calculated hash * $newHash = null; * var_dump(\OC::$server->getHasher()->verify('a', '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', $newHash)); * var_dump($newHash); * * @package OC\Security */ class Hasher implements IHasher { /** @var IConfig */ private $config; /** @var array Options passed to password_hash and password_needs_rehash */ private $options = array(); /** @var string Salt used for legacy passwords */ private $legacySalt = null; /** @var int Current version of the generated hash */ private $currentVersion = 1; /** * @param IConfig $config */ function __construct(IConfig $config) { $this->config = $config; $hashingCost = $this->config->getSystemValue('hashingCost', null); if(!is_null($hashingCost)) { $this->options['cost'] = $hashingCost; } } /** * Hashes a message using PHP's `password_hash` functionality. * Please note that the size of the returned string is not guaranteed * and can be up to 255 characters. * * @param string $message Message to generate hash from * @return string Hash of the message with appended version parameter */ public function hash($message) { return $this->currentVersion . '|' . password_hash($message, PASSWORD_DEFAULT, $this->options); } /** * Get the version and hash from a prefixedHash * @param string $prefixedHash * @return null|array Null if the hash is not prefixed, otherwise array('version' => 1, 'hash' => 'foo') */ protected function splitHash($prefixedHash) { $explodedString = explode('|', $prefixedHash, 2); if(sizeof($explodedString) === 2) { if((int)$explodedString[0] > 0) { return array('version' => (int)$explodedString[0], 'hash' => $explodedString[1]); } } return null; } /** * Verify legacy hashes * @param string $message Message to verify * @param string $hash Assumed hash of the message * @param null|string &$newHash Reference will contain the updated hash * @return bool Whether $hash is a valid hash of $message */ protected function legacyHashVerify($message, $hash, &$newHash = null) { if(empty($this->legacySalt)) { $this->legacySalt = $this->config->getSystemValue('passwordsalt', ''); } // Verify whether it matches a legacy PHPass or SHA1 string $hashLength = strlen($hash); if($hashLength === 60 && password_verify($message.$this->legacySalt, $hash) || $hashLength === 40 && hash_equals($hash, sha1($message))) { $newHash = $this->hash($message); return true; } return false; } /** * Verify V1 hashes * @param string $message Message to verify * @param string $hash Assumed hash of the message * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one. * @return bool Whether $hash is a valid hash of $message */ protected function verifyHashV1($message, $hash, &$newHash = null) { if(password_verify($message, $hash)) { if(password_needs_rehash($hash, PASSWORD_DEFAULT, $this->options)) { $newHash = $this->hash($message); } return true; } return false; } /** * @param string $message Message to verify * @param string $hash Assumed hash of the message * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one. * @return bool Whether $hash is a valid hash of $message */ public function verify($message, $hash, &$newHash = null) { $splittedHash = $this->splitHash($hash); if(isset($splittedHash['version'])) { switch ($splittedHash['version']) { case 1: return $this->verifyHashV1($message, $splittedHash['hash'], $newHash); } } else { return $this->legacyHashVerify($message, $hash, $newHash); } return false; } } private/Security/IdentityProof/Manager.php 0000604 00000007022 15247130453 0014710 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\IdentityProof; use OC\Files\AppData\Factory; use OCP\Files\IAppData; use OCP\IConfig; use OCP\IUser; use OCP\Security\ICrypto; class Manager { /** @var IAppData */ private $appData; /** @var ICrypto */ private $crypto; /** @var IConfig */ private $config; /** * @param Factory $appDataFactory * @param ICrypto $crypto * @param IConfig $config */ public function __construct(Factory $appDataFactory, ICrypto $crypto, IConfig $config ) { $this->appData = $appDataFactory->get('identityproof'); $this->crypto = $crypto; $this->config = $config; } /** * Calls the openssl functions to generate a public and private key. * In a separate function for unit testing purposes. * * @return array [$publicKey, $privateKey] */ protected function generateKeyPair() { $config = [ 'digest_alg' => 'sha512', 'private_key_bits' => 2048, ]; // Generate new key $res = openssl_pkey_new($config); openssl_pkey_export($res, $privateKey); // Extract the public key from $res to $pubKey $publicKey = openssl_pkey_get_details($res); $publicKey = $publicKey['key']; return [$publicKey, $privateKey]; } /** * Generate a key for a given ID * Note: If a key already exists it will be overwritten * * @param string $id key id * @return Key */ protected function generateKey($id) { list($publicKey, $privateKey) = $this->generateKeyPair(); // Write the private and public key to the disk try { $this->appData->newFolder($id); } catch (\Exception $e) {} $folder = $this->appData->getFolder($id); $folder->newFile('private') ->putContent($this->crypto->encrypt($privateKey)); $folder->newFile('public') ->putContent($publicKey); return new Key($publicKey, $privateKey); } /** * Get key for a specific id * * @param string $id * @return Key */ protected function retrieveKey($id) { try { $folder = $this->appData->getFolder($id); $privateKey = $this->crypto->decrypt( $folder->getFile('private')->getContent() ); $publicKey = $folder->getFile('public')->getContent(); return new Key($publicKey, $privateKey); } catch (\Exception $e) { return $this->generateKey($id); } } /** * Get public and private key for $user * * @param IUser $user * @return Key */ public function getKey(IUser $user) { $uid = $user->getUID(); return $this->retrieveKey('user-' . $uid); } /** * Get instance wide public and private key * * @return Key * @throws \RuntimeException */ public function getSystemKey() { $instanceId = $this->config->getSystemValue('instanceid', null); if ($instanceId === null) { throw new \RuntimeException('no instance id!'); } return $this->retrieveKey('system-' . $instanceId); } } private/Security/IdentityProof/Signer.php 0000604 00000005222 15247130453 0014565 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\IdentityProof; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IUser; use OCP\IUserManager; class Signer { /** @var Manager */ private $keyManager; /** @var ITimeFactory */ private $timeFactory; /** @var IUserManager */ private $userManager; /** * @param Manager $keyManager * @param ITimeFactory $timeFactory * @param IUserManager $userManager */ public function __construct(Manager $keyManager, ITimeFactory $timeFactory, IUserManager $userManager) { $this->keyManager = $keyManager; $this->timeFactory = $timeFactory; $this->userManager = $userManager; } /** * Returns a signed blob for $data * * @param string $type * @param array $data * @param IUser $user * @return array ['message', 'signature'] */ public function sign($type, array $data, IUser $user) { $privateKey = $this->keyManager->getKey($user)->getPrivate(); $data = [ 'data' => $data, 'type' => $type, 'signer' => $user->getCloudId(), 'timestamp' => $this->timeFactory->getTime(), ]; openssl_sign(json_encode($data), $signature, $privateKey, OPENSSL_ALGO_SHA512); return [ 'message' => $data, 'signature' => base64_encode($signature), ]; } /** * Whether the data is signed properly * * @param array $data * @return bool */ public function verify(array $data) { if(isset($data['message']) && isset($data['signature']) && isset($data['message']['signer']) ) { $location = strrpos($data['message']['signer'], '@'); $userId = substr($data['message']['signer'], 0, $location); $user = $this->userManager->get($userId); if($user !== null) { $key = $this->keyManager->getKey($user); return (bool)openssl_verify( json_encode($data['message']), base64_decode($data['signature']), $key->getPublic(), OPENSSL_ALGO_SHA512 ); } } return false; } } private/Security/IdentityProof/Key.php 0000604 00000002366 15247130453 0014074 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\IdentityProof; class Key { /** @var string */ private $publicKey; /** @var string */ private $privateKey; /** * @param string $publicKey * @param string $privateKey */ public function __construct($publicKey, $privateKey) { $this->publicKey = $publicKey; $this->privateKey = $privateKey; } public function getPrivate() { return $this->privateKey; } public function getPublic() { return $this->publicKey; } } private/Security/RateLimiting/Limiter.php 0000604 00000006124 15247130453 0014536 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\RateLimiting; use OC\Security\Normalizer\IpAddress; use OC\Security\RateLimiting\Backend\IBackend; use OC\Security\RateLimiting\Exception\RateLimitExceededException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IRequest; use OCP\IUser; use OCP\IUserSession; class Limiter { /** @var IBackend */ private $backend; /** @var ITimeFactory */ private $timeFactory; /** * @param IUserSession $userSession * @param IRequest $request * @param ITimeFactory $timeFactory * @param IBackend $backend */ public function __construct(IUserSession $userSession, IRequest $request, ITimeFactory $timeFactory, IBackend $backend) { $this->backend = $backend; $this->timeFactory = $timeFactory; } /** * @param string $methodIdentifier * @param string $userIdentifier * @param int $period * @param int $limit * @throws RateLimitExceededException */ private function register($methodIdentifier, $userIdentifier, $period, $limit) { $existingAttempts = $this->backend->getAttempts($methodIdentifier, $userIdentifier, (int)$period); if ($existingAttempts >= (int)$limit) { throw new RateLimitExceededException(); } $this->backend->registerAttempt($methodIdentifier, $userIdentifier, $this->timeFactory->getTime()); } /** * Registers attempt for an anonymous request * * @param string $identifier * @param int $anonLimit * @param int $anonPeriod * @param string $ip * @throws RateLimitExceededException */ public function registerAnonRequest($identifier, $anonLimit, $anonPeriod, $ip) { $ipSubnet = (new IpAddress($ip))->getSubnet(); $anonHashIdentifier = hash('sha512', 'anon::' . $identifier . $ipSubnet); $this->register($identifier, $anonHashIdentifier, $anonPeriod, $anonLimit); } /** * Registers attempt for an authenticated request * * @param string $identifier * @param int $userLimit * @param int $userPeriod * @param IUser $user * @throws RateLimitExceededException */ public function registerUserRequest($identifier, $userLimit, $userPeriod, IUser $user) { $userHashIdentifier = hash('sha512', 'user::' . $identifier . $user->getUID()); $this->register($identifier, $userHashIdentifier, $userPeriod, $userLimit); } } private/Security/RateLimiting/Backend/MemoryCache.php 0000604 00000006141 15247130453 0016653 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\RateLimiting\Backend; use OCP\AppFramework\Utility\ITimeFactory; use OCP\ICache; use OCP\ICacheFactory; /** * Class MemoryCache uses the configured distributed memory cache for storing * rate limiting data. * * @package OC\Security\RateLimiting\Backend */ class MemoryCache implements IBackend { /** @var ICache */ private $cache; /** @var ITimeFactory */ private $timeFactory; /** * @param ICacheFactory $cacheFactory * @param ITimeFactory $timeFactory */ public function __construct(ICacheFactory $cacheFactory, ITimeFactory $timeFactory) { $this->cache = $cacheFactory->create(__CLASS__); $this->timeFactory = $timeFactory; } /** * @param string $methodIdentifier * @param string $userIdentifier * @return string */ private function hash($methodIdentifier, $userIdentifier) { return hash('sha512', $methodIdentifier . $userIdentifier); } /** * @param string $identifier * @return array */ private function getExistingAttempts($identifier) { $cachedAttempts = json_decode($this->cache->get($identifier), true); if(is_array($cachedAttempts)) { return $cachedAttempts; } return []; } /** * {@inheritDoc} */ public function getAttempts($methodIdentifier, $userIdentifier, $seconds) { $identifier = $this->hash($methodIdentifier, $userIdentifier); $existingAttempts = $this->getExistingAttempts($identifier); $count = 0; $currentTime = $this->timeFactory->getTime(); /** @var array $existingAttempts */ foreach ($existingAttempts as $attempt) { if(($attempt + $seconds) > $currentTime) { $count++; } } return $count; } /** * {@inheritDoc} */ public function registerAttempt($methodIdentifier, $userIdentifier, $period) { $identifier = $this->hash($methodIdentifier, $userIdentifier); $existingAttempts = $this->getExistingAttempts($identifier); $currentTime = $this->timeFactory->getTime(); // Unset all attempts older than $period foreach ($existingAttempts as $key => $attempt) { if(($attempt + $period) < $currentTime) { unset($existingAttempts[$key]); } } $existingAttempts = array_values($existingAttempts); // Store the new attempt $existingAttempts[] = (string)$currentTime; $this->cache->set($identifier, json_encode($existingAttempts)); } } private/Security/RateLimiting/Backend/IBackend.php 0000604 00000003514 15247130453 0016120 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\RateLimiting\Backend; /** * Interface IBackend defines a storage backend for the rate limiting data. It * should be noted that writing and reading rate limiting data is an expensive * operation and one should thus make sure to only use sufficient fast backends. * * @package OC\Security\RateLimiting\Backend */ interface IBackend { /** * Gets the amount of attempts within the last specified seconds * * @param string $methodIdentifier Identifier for the method * @param string $userIdentifier Identifier for the user * @param int $seconds Seconds to look back at * @return int */ public function getAttempts($methodIdentifier, $userIdentifier, $seconds); /** * Registers an attempt * * @param string $methodIdentifier Identifier for the method * @param string $userIdentifier Identifier for the user * @param int $period Period in seconds how long this attempt should be stored */ public function registerAttempt($methodIdentifier, $userIdentifier, $period); } private/Security/RateLimiting/Exception/RateLimitExceededException.php 0000604 00000002156 15247130453 0022270 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Security\RateLimiting\Exception; use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; use OCP\AppFramework\Http; class RateLimitExceededException extends SecurityException { public function __construct() { parent::__construct('Rate limit exceeded', Http::STATUS_TOO_MANY_REQUESTS); } } private/NaturalSort.php 0000604 00000007773 15247130453 0011223 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author AW-UC <git@a-wesemann.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class NaturalSort { private static $instance; private $collator; private $cache = array(); /** * Instantiate a new \OC\NaturalSort instance. * @param object $injectedCollator */ public function __construct($injectedCollator = null) { // inject an instance of \Collator('en_US') to force using the php5-intl Collator // or inject an instance of \OC\NaturalSort_DefaultCollator to force using Owncloud's default collator if (isset($injectedCollator)) { $this->collator = $injectedCollator; \OCP\Util::writeLog('core', 'forced use of '.get_class($injectedCollator), \OCP\Util::DEBUG); } } /** * Split the given string in chunks of numbers and strings * @param string $t string * @return array of strings and number chunks */ private function naturalSortChunkify($t) { // Adapted and ported to PHP from // http://my.opera.com/GreyWyvern/blog/show.dml/1671288 if (isset($this->cache[$t])) { return $this->cache[$t]; } $tz = array(); $x = 0; $y = -1; $n = null; while (isset($t[$x])) { $c = $t[$x]; // only include the dot in strings $m = ((!$n && $c === '.') || ($c >= '0' && $c <= '9')); if ($m !== $n) { // next chunk $y++; $tz[$y] = ''; $n = $m; } $tz[$y] .= $c; $x++; } $this->cache[$t] = $tz; return $tz; } /** * Returns the string collator * @return \Collator string collator */ private function getCollator() { if (!isset($this->collator)) { // looks like the default is en_US_POSIX which yields wrong sorting with // German umlauts, so using en_US instead if (class_exists('Collator')) { $this->collator = new \Collator('en_US'); } else { $this->collator = new \OC\NaturalSort_DefaultCollator(); } } return $this->collator; } /** * Compare two strings to provide a natural sort * @param string $a first string to compare * @param string $b second string to compare * @return int -1 if $b comes before $a, 1 if $a comes before $b * or 0 if the strings are identical */ public function compare($a, $b) { // Needed because PHP doesn't sort correctly when numbers are enclosed in // parenthesis, even with NUMERIC_COLLATION enabled. // For example it gave ["test (2).txt", "test.txt"] // instead of ["test.txt", "test (2).txt"] $aa = self::naturalSortChunkify($a); $bb = self::naturalSortChunkify($b); for ($x = 0; isset($aa[$x]) && isset($bb[$x]); $x++) { $aChunk = $aa[$x]; $bChunk = $bb[$x]; if ($aChunk !== $bChunk) { // test first character (character comparison, not number comparison) if ($aChunk[0] >= '0' && $aChunk[0] <= '9' && $bChunk[0] >= '0' && $bChunk[0] <= '9') { $aNum = (int)$aChunk; $bNum = (int)$bChunk; return $aNum - $bNum; } return self::getCollator()->compare($aChunk, $bChunk); } } return count($aa) - count($bb); } /** * Returns a singleton * @return \OC\NaturalSort instance */ public static function getInstance() { if (!isset(self::$instance)) { self::$instance = new \OC\NaturalSort(); } return self::$instance; } } private/OCS/Result.php 0000604 00000007105 15247130453 0010634 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\OCS; class Result { /** @var array */ protected $data; /** @var null|string */ protected $message; /** @var int */ protected $statusCode; /** @var integer */ protected $items; /** @var integer */ protected $perPage; /** @var array */ private $headers = []; /** * create the OCS_Result object * @param mixed $data the data to return * @param int $code * @param null|string $message * @param array $headers */ public function __construct($data = null, $code = 100, $message = null, $headers = []) { if ($data === null) { $this->data = array(); } elseif (!is_array($data)) { $this->data = array($this->data); } else { $this->data = $data; } $this->statusCode = $code; $this->message = $message; $this->headers = $headers; } /** * optionally set the total number of items available * @param int $items */ public function setTotalItems($items) { $this->items = $items; } /** * optionally set the the number of items per page * @param int $items */ public function setItemsPerPage($items) { $this->perPage = $items; } /** * get the status code * @return int */ public function getStatusCode() { return $this->statusCode; } /** * get the meta data for the result * @return array */ public function getMeta() { $meta = array(); $meta['status'] = $this->succeeded() ? 'ok' : 'failure'; $meta['statuscode'] = $this->statusCode; $meta['message'] = $this->message; if(isset($this->items)) { $meta['totalitems'] = $this->items; } if(isset($this->perPage)) { $meta['itemsperpage'] = $this->perPage; } return $meta; } /** * get the result data * @return array */ public function getData() { return $this->data; } /** * return bool Whether the method succeeded * @return bool */ public function succeeded() { return ($this->statusCode == 100); } /** * Adds a new header to the response * @param string $name The name of the HTTP header * @param string $value The value, null will delete it * @return $this */ public function addHeader($name, $value) { $name = trim($name); // always remove leading and trailing whitespace // to be able to reliably check for security // headers if(is_null($value)) { unset($this->headers[$name]); } else { $this->headers[$name] = $value; } return $this; } /** * Returns the set headers * @return array the headers */ public function getHeaders() { return $this->headers; } } private/OCS/CoreCapabilities.php 0000604 00000002647 15247130453 0012566 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\OCS; use OCP\Capabilities\ICapability; use OCP\IConfig; /** * Class Capabilities * * @package OC\OCS */ class CoreCapabilities implements ICapability { /** @var IConfig */ private $config; /** * @param IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * Return this classes capabilities * * @return array */ public function getCapabilities() { return [ 'core' => [ 'pollinterval' => $this->config->getSystemValue('pollinterval', 60), 'webdav-root' => $this->config->getSystemValue('webdav-root', 'remote.php/webdav'), ] ]; } } private/OCS/PrivateData.php 0000604 00000007526 15247130453 0011571 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Frank Karlitschek <frank@karlitschek.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\OCS; class PrivateData { /** * read keys * test: curl http://login:passwd@oc/core/ocs/v1.php/privatedata/getattribute/testy/123 * test: curl http://login:passwd@oc/core/ocs/v1.php/privatedata/getattribute/testy * @param array $parameters The OCS parameter * @return \OC_OCS_Result */ public static function get($parameters) { $user = \OC_User::getUser(); $app = addslashes(strip_tags($parameters['app'])); $key = isset($parameters['key']) ? addslashes(strip_tags($parameters['key'])) : null; if(empty($key)) { $query = \OCP\DB::prepare('SELECT `key`, `app`, `value` FROM `*PREFIX*privatedata` WHERE `user` = ? AND `app` = ? '); $result = $query->execute(array($user, $app)); } else { $query = \OCP\DB::prepare('SELECT `key`, `app`, `value` FROM `*PREFIX*privatedata` WHERE `user` = ? AND `app` = ? AND `key` = ? '); $result = $query->execute(array($user, $app, $key)); } $xml = array(); while ($row = $result->fetchRow()) { $data=array(); $data['key']=$row['key']; $data['app']=$row['app']; $data['value']=$row['value']; $xml[] = $data; } return new Result($xml); } /** * set a key * test: curl http://login:passwd@oc/core/ocs/v1.php/privatedata/setattribute/testy/123 --data "value=foobar" * @param array $parameters The OCS parameter * @return \OC_OCS_Result */ public static function set($parameters) { $user = \OC_User::getUser(); $app = addslashes(strip_tags($parameters['app'])); $key = addslashes(strip_tags($parameters['key'])); $value = (string)$_POST['value']; // update in DB $query = \OCP\DB::prepare('UPDATE `*PREFIX*privatedata` SET `value` = ? WHERE `user` = ? AND `app` = ? AND `key` = ?'); $numRows = $query->execute(array($value, $user, $app, $key)); if ($numRows === false || $numRows === 0) { // store in DB $query = \OCP\DB::prepare('INSERT INTO `*PREFIX*privatedata` (`user`, `app`, `key`, `value`)' . ' VALUES(?, ?, ?, ?)'); $query->execute(array($user, $app, $key, $value)); } return new Result(null, 100); } /** * delete a key * test: curl http://login:passwd@oc/core/ocs/v1.php/privatedata/deleteattribute/testy/123 --data "post=1" * @param array $parameters The OCS parameter * @return \OC_OCS_Result */ public static function delete($parameters) { $user = \OC_User::getUser(); if (!isset($parameters['app']) or !isset($parameters['key'])) { //key and app are NOT optional here return new Result(null, 101); } $app = addslashes(strip_tags($parameters['app'])); $key = addslashes(strip_tags($parameters['key'])); // delete in DB $query = \OCP\DB::prepare('DELETE FROM `*PREFIX*privatedata` WHERE `user` = ? AND `app` = ? AND `key` = ? '); $query->execute(array($user, $app, $key )); return new Result(null, 100); } } private/OCS/Provider.php 0000604 00000006331 15247130453 0011150 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\OCS; class Provider extends \OCP\AppFramework\Controller { /** @var \OCP\App\IAppManager */ private $appManager; /** * @param string $appName * @param \OCP\IRequest $request * @param \OCP\App\IAppManager $appManager */ public function __construct($appName, \OCP\IRequest $request, \OCP\App\IAppManager $appManager) { parent::__construct($appName, $request); $this->appManager = $appManager; } /** * @return \OCP\AppFramework\Http\JSONResponse */ public function buildProviderList() { $services = [ 'PRIVATE_DATA' => [ 'version' => 1, 'endpoints' => [ 'store' => '/ocs/v2.php/privatedata/setattribute', 'read' => '/ocs/v2.php/privatedata/getattribute', 'delete' => '/ocs/v2.php/privatedata/deleteattribute', ], ], ]; if($this->appManager->isEnabledForUser('files_sharing')) { $services['SHARING'] = [ 'version' => 1, 'endpoints' => [ 'share' => '/ocs/v2.php/apps/files_sharing/api/v1/shares', ], ]; $services['FEDERATED_SHARING'] = [ 'version' => 1, 'endpoints' => [ 'share' => '/ocs/v2.php/cloud/shares', 'webdav' => '/public.php/webdav/', ], ]; } if ($this->appManager->isEnabledForUser('federation')) { if (isset($services['FEDERATED_SHARING'])) { $services['FEDERATED_SHARING']['endpoints']['shared-secret'] = '/ocs/v2.php/cloud/shared-secret'; $services['FEDERATED_SHARING']['endpoints']['system-address-book'] = '/remote.php/dav/addressbooks/system/system/system'; $services['FEDERATED_SHARING']['endpoints']['carddav-user'] = 'system'; } else { $services['FEDERATED_SHARING'] = [ 'version' => 1, 'endpoints' => [ 'shared-secret' => '/ocs/v2.php/cloud/shared-secret', 'system-address-book' => '/remote.php/dav/addressbooks/system/system/system', 'carddav-user' => 'system' ], ]; } } if($this->appManager->isEnabledForUser('activity')) { $services['ACTIVITY'] = [ 'version' => 1, 'endpoints' => [ 'list' => '/ocs/v2.php/cloud/activity', ], ]; } if($this->appManager->isEnabledForUser('provisioning_api')) { $services['PROVISIONING'] = [ 'version' => 1, 'endpoints' => [ 'user' => '/ocs/v2.php/cloud/users', 'groups' => '/ocs/v2.php/cloud/groups', 'apps' => '/ocs/v2.php/cloud/apps', ], ]; } return new \OCP\AppFramework\Http\JSONResponse([ 'version' => 2, 'services' => $services, ]); } } private/OCS/Exception.php 0000604 00000001757 15247130453 0011323 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\OCS; class Exception extends \Exception { public function __construct(Result $result) { $this->result = $result; } public function getResult() { return $this->result; } } private/OCS/DiscoveryService.php 0000604 00000006373 15247130453 0012654 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\OCS; use OCP\AppFramework\Http; use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\ICache; use OCP\ICacheFactory; use OCP\OCS\IDiscoveryService; class DiscoveryService implements IDiscoveryService { /** @var ICache */ private $cache; /** @var IClient */ private $client; /** * @param ICacheFactory $cacheFactory * @param IClientService $clientService */ public function __construct(ICacheFactory $cacheFactory, IClientService $clientService ) { $this->cache = $cacheFactory->create('ocs-discovery'); $this->client = $clientService->newClient(); } /** * Discover OCS end-points * * If no valid discovery data is found the defaults are returned * * @param string $remote * @param string $service the service you want to discover * @return array */ public function discover($remote, $service) { // Check the cache first $cacheData = $this->cache->get($remote . '#' . $service); if($cacheData) { return json_decode($cacheData, true); } $discoveredServices = []; // query the remote server for available services try { $response = $this->client->get($remote . '/ocs-provider/', [ 'timeout' => 10, 'connect_timeout' => 10, ]); if($response->getStatusCode() === Http::STATUS_OK) { $decodedServices = json_decode($response->getBody(), true); $discoveredServices = $this->getEndpoints($decodedServices, $service); } } catch (\Exception $e) { // if we couldn't discover the service or any end-points we return a empty array return []; } // Write into cache $this->cache->set($remote . '#' . $service, json_encode($discoveredServices)); return $discoveredServices; } /** * get requested end-points from the requested service * * @param $decodedServices * @param $service * @return array */ protected function getEndpoints($decodedServices, $service) { $discoveredServices = []; if(is_array($decodedServices) && isset($decodedServices['services'][$service]['endpoints']) ) { foreach ($decodedServices['services'][$service]['endpoints'] as $endpoint => $url) { if($this->isSafeUrl($url)) { $discoveredServices[$endpoint] = $url; } } } return $discoveredServices; } /** * Returns whether the specified URL includes only safe characters, if not * returns false * * @param string $url * @return bool */ protected function isSafeUrl($url) { return (bool)preg_match('/^[\/\.\-A-Za-z0-9]+$/', $url); } } private/PreviewManager.php 0000604 00000030625 15247130453 0011651 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Olivier Paroz <github@oparoz.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OC\Preview\Generator; use OC\Preview\GeneratorHelper; use OCP\Files\File; use OCP\Files\IAppData; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\IConfig; use OCP\IPreview; use OCP\Preview\IProvider; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class PreviewManager implements IPreview { /** @var IConfig */ protected $config; /** @var IRootFolder */ protected $rootFolder; /** @var IAppData */ protected $appData; /** @var EventDispatcherInterface */ protected $eventDispatcher; /** @var Generator */ private $generator; /** @var bool */ protected $providerListDirty = false; /** @var bool */ protected $registeredCoreProviders = false; /** @var array */ protected $providers = []; /** @var array mime type => support status */ protected $mimeTypeSupportMap = []; /** @var array */ protected $defaultProviders; /** @var string */ protected $userId; /** * PreviewManager constructor. * * @param IConfig $config * @param IRootFolder $rootFolder * @param IAppData $appData * @param EventDispatcherInterface $eventDispatcher * @param string $userId */ public function __construct(IConfig $config, IRootFolder $rootFolder, IAppData $appData, EventDispatcherInterface $eventDispatcher, $userId) { $this->config = $config; $this->rootFolder = $rootFolder; $this->appData = $appData; $this->eventDispatcher = $eventDispatcher; $this->userId = $userId; } /** * In order to improve lazy loading a closure can be registered which will be * called in case preview providers are actually requested * * $callable has to return an instance of \OCP\Preview\IProvider * * @param string $mimeTypeRegex Regex with the mime types that are supported by this provider * @param \Closure $callable * @return void */ public function registerProvider($mimeTypeRegex, \Closure $callable) { if (!$this->config->getSystemValue('enable_previews', true)) { return; } if (!isset($this->providers[$mimeTypeRegex])) { $this->providers[$mimeTypeRegex] = []; } $this->providers[$mimeTypeRegex][] = $callable; $this->providerListDirty = true; } /** * Get all providers * @return array */ public function getProviders() { if (!$this->config->getSystemValue('enable_previews', true)) { return []; } $this->registerCoreProviders(); if ($this->providerListDirty) { $keys = array_map('strlen', array_keys($this->providers)); array_multisort($keys, SORT_DESC, $this->providers); $this->providerListDirty = false; } return $this->providers; } /** * Does the manager have any providers * @return bool */ public function hasProviders() { $this->registerCoreProviders(); return !empty($this->providers); } /** * return a preview of a file * * @param string $file The path to the file where you want a thumbnail from * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image * @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly * @return \OCP\IImage * @deprecated 11 Use getPreview */ public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false) { try { $userRoot = $this->rootFolder->getUserFolder($this->userId)->getParent(); $node = $userRoot->get($file); if (!($file instanceof File)) { throw new NotFoundException(); } $preview = $this->getPreview($node, $maxX, $maxY); } catch (\Exception $e) { return new \OC_Image(); } return new \OC_Image($preview->getContent()); } /** * Returns a preview of a file * * The cache is searched first and if nothing usable was found then a preview is * generated by one of the providers * * @param File $file * @param int $width * @param int $height * @param bool $crop * @param string $mode * @param string $mimeType * @return ISimpleFile * @throws NotFoundException * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) * @since 11.0.0 - \InvalidArgumentException was added in 12.0.0 */ public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) { if ($this->generator === null) { $this->generator = new Generator( $this->config, $this, $this->appData, new GeneratorHelper( $this->rootFolder ), $this->eventDispatcher ); } return $this->generator->getPreview($file, $width, $height, $crop, $mode, $mimeType); } /** * returns true if the passed mime type is supported * * @param string $mimeType * @return boolean */ public function isMimeSupported($mimeType = '*') { if (!$this->config->getSystemValue('enable_previews', true)) { return false; } if (isset($this->mimeTypeSupportMap[$mimeType])) { return $this->mimeTypeSupportMap[$mimeType]; } $this->registerCoreProviders(); $providerMimeTypes = array_keys($this->providers); foreach ($providerMimeTypes as $supportedMimeType) { if (preg_match($supportedMimeType, $mimeType)) { $this->mimeTypeSupportMap[$mimeType] = true; return true; } } $this->mimeTypeSupportMap[$mimeType] = false; return false; } /** * Check if a preview can be generated for a file * * @param \OCP\Files\FileInfo $file * @return bool */ public function isAvailable(\OCP\Files\FileInfo $file) { if (!$this->config->getSystemValue('enable_previews', true)) { return false; } $this->registerCoreProviders(); if (!$this->isMimeSupported($file->getMimetype())) { return false; } $mount = $file->getMountPoint(); if ($mount and !$mount->getOption('previews', true)){ return false; } foreach ($this->providers as $supportedMimeType => $providers) { if (preg_match($supportedMimeType, $file->getMimetype())) { foreach ($providers as $closure) { $provider = $closure(); if (!($provider instanceof IProvider)) { continue; } /** @var $provider IProvider */ if ($provider->isAvailable($file)) { return true; } } } } return false; } /** * List of enabled default providers * * The following providers are enabled by default: * - OC\Preview\PNG * - OC\Preview\JPEG * - OC\Preview\GIF * - OC\Preview\BMP * - OC\Preview\XBitmap * - OC\Preview\MarkDown * - OC\Preview\MP3 * - OC\Preview\TXT * * The following providers are disabled by default due to performance or privacy concerns: * - OC\Preview\Font * - OC\Preview\Illustrator * - OC\Preview\Movie * - OC\Preview\MSOfficeDoc * - OC\Preview\MSOffice2003 * - OC\Preview\MSOffice2007 * - OC\Preview\OpenDocument * - OC\Preview\PDF * - OC\Preview\Photoshop * - OC\Preview\Postscript * - OC\Preview\StarOffice * - OC\Preview\SVG * - OC\Preview\TIFF * * @return array */ protected function getEnabledDefaultProvider() { if ($this->defaultProviders !== null) { return $this->defaultProviders; } $imageProviders = [ 'OC\Preview\PNG', 'OC\Preview\JPEG', 'OC\Preview\GIF', 'OC\Preview\BMP', 'OC\Preview\XBitmap' ]; $this->defaultProviders = $this->config->getSystemValue('enabledPreviewProviders', array_merge([ 'OC\Preview\MarkDown', 'OC\Preview\MP3', 'OC\Preview\TXT', ], $imageProviders)); if (in_array('OC\Preview\Image', $this->defaultProviders)) { $this->defaultProviders = array_merge($this->defaultProviders, $imageProviders); } $this->defaultProviders = array_unique($this->defaultProviders); return $this->defaultProviders; } /** * Register the default providers (if enabled) * * @param string $class * @param string $mimeType */ protected function registerCoreProvider($class, $mimeType, $options = []) { if (in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) { $this->registerProvider($mimeType, function () use ($class, $options) { return new $class($options); }); } } /** * Register the default providers (if enabled) */ protected function registerCoreProviders() { if ($this->registeredCoreProviders) { return; } $this->registeredCoreProviders = true; $this->registerCoreProvider('OC\Preview\TXT', '/text\/plain/'); $this->registerCoreProvider('OC\Preview\MarkDown', '/text\/(x-)?markdown/'); $this->registerCoreProvider('OC\Preview\PNG', '/image\/png/'); $this->registerCoreProvider('OC\Preview\JPEG', '/image\/jpeg/'); $this->registerCoreProvider('OC\Preview\GIF', '/image\/gif/'); $this->registerCoreProvider('OC\Preview\BMP', '/image\/bmp/'); $this->registerCoreProvider('OC\Preview\XBitmap', '/image\/x-xbitmap/'); $this->registerCoreProvider('OC\Preview\MP3', '/audio\/mpeg/'); // SVG, Office and Bitmap require imagick if (extension_loaded('imagick')) { $checkImagick = new \Imagick(); $imagickProviders = [ 'SVG' => ['mimetype' => '/image\/svg\+xml/', 'class' => '\OC\Preview\SVG'], 'TIFF' => ['mimetype' => '/image\/tiff/', 'class' => '\OC\Preview\TIFF'], 'PDF' => ['mimetype' => '/application\/pdf/', 'class' => '\OC\Preview\PDF'], 'AI' => ['mimetype' => '/application\/illustrator/', 'class' => '\OC\Preview\Illustrator'], 'PSD' => ['mimetype' => '/application\/x-photoshop/', 'class' => '\OC\Preview\Photoshop'], 'EPS' => ['mimetype' => '/application\/postscript/', 'class' => '\OC\Preview\Postscript'], 'TTF' => ['mimetype' => '/application\/(?:font-sfnt|x-font$)/', 'class' => '\OC\Preview\Font'], ]; foreach ($imagickProviders as $queryFormat => $provider) { $class = $provider['class']; if (!in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) { continue; } if (count($checkImagick->queryFormats($queryFormat)) === 1) { $this->registerCoreProvider($class, $provider['mimetype']); } } if (count($checkImagick->queryFormats('PDF')) === 1) { if (\OC_Helper::is_function_enabled('shell_exec')) { $officeFound = is_string($this->config->getSystemValue('preview_libreoffice_path', null)); if (!$officeFound) { //let's see if there is libreoffice or openoffice on this machine $whichLibreOffice = shell_exec('command -v libreoffice'); $officeFound = !empty($whichLibreOffice); if (!$officeFound) { $whichOpenOffice = shell_exec('command -v openoffice'); $officeFound = !empty($whichOpenOffice); } } if ($officeFound) { $this->registerCoreProvider('\OC\Preview\MSOfficeDoc', '/application\/msword/'); $this->registerCoreProvider('\OC\Preview\MSOffice2003', '/application\/vnd.ms-.*/'); $this->registerCoreProvider('\OC\Preview\MSOffice2007', '/application\/vnd.openxmlformats-officedocument.*/'); $this->registerCoreProvider('\OC\Preview\OpenDocument', '/application\/vnd.oasis.opendocument.*/'); $this->registerCoreProvider('\OC\Preview\StarOffice', '/application\/vnd.sun.xml.*/'); } } } } // Video requires avconv or ffmpeg if (in_array('OC\Preview\Movie', $this->getEnabledDefaultProvider())) { $avconvBinary = \OC_Helper::findBinaryPath('avconv'); $ffmpegBinary = ($avconvBinary) ? null : \OC_Helper::findBinaryPath('ffmpeg'); if ($avconvBinary || $ffmpegBinary) { // FIXME // a bit hacky but didn't want to use subclasses \OC\Preview\Movie::$avconvBinary = $avconvBinary; \OC\Preview\Movie::$ffmpegBinary = $ffmpegBinary; $this->registerCoreProvider('\OC\Preview\Movie', '/video\/.*/'); } } } } private/Share/Constants.php 0000604 00000003304 15247130453 0011745 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share; class Constants { const SHARE_TYPE_USER = 0; const SHARE_TYPE_GROUP = 1; const SHARE_TYPE_LINK = 3; const SHARE_TYPE_EMAIL = 4; const SHARE_TYPE_CONTACT = 5; // ToDo Check if it is still in use otherwise remove it const SHARE_TYPE_REMOTE = 6; const SHARE_TYPE_CIRCLE = 7; const SHARE_TYPE_GUEST = 8; const FORMAT_NONE = -1; const FORMAT_STATUSES = -2; const FORMAT_SOURCES = -3; // ToDo Check if it is still in use otherwise remove it const RESPONSE_FORMAT = 'json'; // default resonse format for ocs calls const TOKEN_LENGTH = 15; // old (oc7) length is 32, keep token length in db at least that for compatibility protected static $shareTypeUserAndGroups = -1; protected static $shareTypeGroupUserUnique = 2; protected static $backends = array(); protected static $backendTypes = array(); protected static $isResharingAllowed; } private/Share/Share.php 0000604 00000323521 15247130453 0011041 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Reiter <ockham@raz.or.at> * @author Björn Schießle <bjoern@schiessle.org> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Christoph Wurst <christoph@owncloud.com> * @author Daniel Hansson <enoch85@gmail.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Michael Kuhn <suraia@ikkoku.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sebastian Döll <sebastian.doell@libasys.de> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Torben Dannhauer <torben@dannhauer.de> * @author Vincent Petry <pvince81@owncloud.com> * @author Volkan Gezer <volkangezer@gmail.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share; use OC\Files\Filesystem; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\ILogger; use OCP\IUserManager; use OCP\IUserSession; use OCP\IDBConnection; use OCP\IConfig; use OCP\Util; /** * This class provides the ability for apps to share their content between users. * Apps must create a backend class that implements OCP\Share_Backend and register it with this class. * * It provides the following hooks: * - post_shared */ class Share extends Constants { /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask * Construct permissions for share() and setPermissions with Or (|) e.g. * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE * * Check if permission is granted with And (&) e.g. Check if delete is * granted: if ($permissions & PERMISSION_DELETE) * * Remove permissions with And (&) and Not (~) e.g. Remove the update * permission: $permissions &= ~PERMISSION_UPDATE * * Apps are required to handle permissions on their own, this class only * stores and manages the permissions of shares * @see lib/public/constants.php */ /** * Register a sharing backend class that implements OCP\Share_Backend for an item type * @param string $itemType Item type * @param string $class Backend class * @param string $collectionOf (optional) Depends on item type * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files * @return boolean true if backend is registered or false if error */ public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) { if (self::isEnabled()) { if (!isset(self::$backendTypes[$itemType])) { self::$backendTypes[$itemType] = array( 'class' => $class, 'collectionOf' => $collectionOf, 'supportedFileExtensions' => $supportedFileExtensions ); if(count(self::$backendTypes) === 1) { Util::addScript('core', 'merged-share-backend'); \OC_Util::addStyle('core', 'share'); } return true; } \OCP\Util::writeLog('OCP\Share', 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class'] .' is already registered for '.$itemType, \OCP\Util::WARN); } return false; } /** * Check if the Share API is enabled * @return boolean true if enabled or false * * The Share API is enabled by default if not configured */ public static function isEnabled() { if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_enabled', 'yes') == 'yes') { return true; } return false; } /** * Find which users can access a shared item * @param string $path to the file * @param string $ownerUser owner of the file * @param IUserManager $userManager * @param ILogger $logger * @param boolean $includeOwner include owner to the list of users with access to the file * @param boolean $returnUserPaths Return an array with the user => path map * @param boolean $recursive take all parent folders into account (default true) * @return array * @note $path needs to be relative to user data dir, e.g. 'file.txt' * not '/admin/data/file.txt' * @throws \OC\User\NoUserException */ public static function getUsersSharingFile($path, $ownerUser, IUserManager $userManager, ILogger $logger, $includeOwner = false, $returnUserPaths = false, $recursive = true) { $userObject = $userManager->get($ownerUser); if (is_null($userObject)) { $logger->error( sprintf( 'Backends provided no user object for %s', $ownerUser ), [ 'app' => 'files', ] ); throw new \OC\User\NoUserException('Backends provided no user object'); } $ownerUser = $userObject->getUID(); Filesystem::initMountPoints($ownerUser); $shares = $sharePaths = $fileTargets = array(); $publicShare = false; $remoteShare = false; $source = -1; $cache = $mountPath = false; $view = new \OC\Files\View('/' . $ownerUser . '/files'); $meta = $view->getFileInfo($path); if ($meta) { $path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files')); } else { // if the file doesn't exists yet we start with the parent folder $meta = $view->getFileInfo(dirname($path)); } if($meta !== false) { $source = $meta['fileid']; $cache = new \OC\Files\Cache\Cache($meta['storage']); $mountPath = $meta->getMountPoint()->getMountPoint(); if ($mountPath !== false) { $mountPath = substr($mountPath, strlen('/' . $ownerUser . '/files')); } } $paths = []; while ($source !== -1) { // Fetch all shares with another user if (!$returnUserPaths) { $query = \OC_DB::prepare( 'SELECT `share_with`, `file_source`, `file_target` FROM `*PREFIX*share` WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' ); $result = $query->execute(array($source, self::SHARE_TYPE_USER)); } else { $query = \OC_DB::prepare( 'SELECT `share_with`, `file_source`, `file_target` FROM `*PREFIX*share` WHERE `item_source` = ? AND `share_type` IN (?, ?) AND `item_type` IN (\'file\', \'folder\')' ); $result = $query->execute(array($source, self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique)); } if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR); } else { while ($row = $result->fetchRow()) { $shares[] = $row['share_with']; if ($returnUserPaths) { $fileTargets[(int) $row['file_source']][$row['share_with']] = $row; } } } // We also need to take group shares into account $query = \OC_DB::prepare( 'SELECT `share_with`, `file_source`, `file_target` FROM `*PREFIX*share` WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' ); $result = $query->execute(array($source, self::SHARE_TYPE_GROUP)); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR); } else { $groupManager = \OC::$server->getGroupManager(); while ($row = $result->fetchRow()) { $usersInGroup = []; $group = $groupManager->get($row['share_with']); if ($group) { $users = $group->searchUsers('', -1, 0); $userIds = array(); foreach ($users as $user) { $userIds[] = $user->getUID(); } $usersInGroup = $userIds; } $shares = array_merge($shares, $usersInGroup); if ($returnUserPaths) { foreach ($usersInGroup as $user) { if (!isset($fileTargets[(int) $row['file_source']][$user])) { // When the user already has an entry for this file source // the file is either shared directly with him as well, or // he has an exception entry (because of naming conflict). $fileTargets[(int) $row['file_source']][$user] = $row; } } } } } //check for public link shares if (!$publicShare) { $query = \OC_DB::prepare(' SELECT `share_with` FROM `*PREFIX*share` WHERE `item_source` = ? AND `share_type` IN (?, ?) AND `item_type` IN (\'file\', \'folder\')', 1 ); $result = $query->execute(array($source, self::SHARE_TYPE_LINK, self::SHARE_TYPE_EMAIL)); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR); } else { if ($result->fetchRow()) { $publicShare = true; } } } //check for remote share if (!$remoteShare) { $query = \OC_DB::prepare(' SELECT `share_with` FROM `*PREFIX*share` WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')', 1 ); $result = $query->execute(array($source, self::SHARE_TYPE_REMOTE)); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR); } else { if ($result->fetchRow()) { $remoteShare = true; } } } // let's get the parent for the next round $meta = $cache->get((int)$source); if ($recursive === true && $meta !== false) { $paths[$source] = $meta['path']; $source = (int)$meta['parent']; } else { $source = -1; } } // Include owner in list of users, if requested if ($includeOwner) { $shares[] = $ownerUser; } if ($returnUserPaths) { $fileTargetIDs = array_keys($fileTargets); $fileTargetIDs = array_unique($fileTargetIDs); if (!empty($fileTargetIDs)) { $query = \OC_DB::prepare( 'SELECT `fileid`, `path` FROM `*PREFIX*filecache` WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')' ); $result = $query->execute(); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR); } else { while ($row = $result->fetchRow()) { foreach ($fileTargets[$row['fileid']] as $uid => $shareData) { if ($mountPath !== false) { $sharedPath = $shareData['file_target']; $sharedPath .= substr($path, strlen($mountPath) + strlen($paths[$row['fileid']])); $sharePaths[$uid] = $sharedPath; } else { $sharedPath = $shareData['file_target']; $sharedPath .= substr($path, strlen($row['path']) -5); $sharePaths[$uid] = $sharedPath; } } } $result->closeCursor(); } } if ($includeOwner) { $sharePaths[$ownerUser] = $path; } else { unset($sharePaths[$ownerUser]); } return $sharePaths; } return array('users' => array_unique($shares), 'public' => $publicShare, 'remote' => $remoteShare); } /** * Get the items of item type shared with the current user * @param string $itemType * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters (optional) * @param int $limit Number of items to return (optional) Returns all by default * @param boolean $includeCollections (optional) * @return mixed Return depends on format */ public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, $parameters, $limit, $includeCollections); } /** * Get the items of item type shared with a user * @param string $itemType * @param string $user id for which user we want the shares * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters (optional) * @param int $limit Number of items to return (optional) Returns all by default * @param boolean $includeCollections (optional) * @return mixed Return depends on format */ public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format, $parameters, $limit, $includeCollections); } /** * Get the item of item type shared with the current user * @param string $itemType * @param string $itemTarget * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters (optional) * @param boolean $includeCollections (optional) * @return mixed Return depends on format */ public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, $parameters, 1, $includeCollections); } /** * Get the item of item type shared with a given user by source * @param string $itemType * @param string $itemSource * @param string $user User to whom the item was shared * @param string $owner Owner of the share * @param int $shareType only look for a specific share type * @return array Return list of items with file_target, permissions and expiration */ public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) { $shares = array(); $fileDependent = false; $where = 'WHERE'; $fileDependentWhere = ''; if ($itemType === 'file' || $itemType === 'folder') { $fileDependent = true; $column = 'file_source'; $fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` '; $fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` '; } else { $column = 'item_source'; } $select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent); $where .= ' `' . $column . '` = ? AND `item_type` = ? '; $arguments = array($itemSource, $itemType); // for link shares $user === null if ($user !== null) { $where .= ' AND `share_with` = ? '; $arguments[] = $user; } if ($shareType !== null) { $where .= ' AND `share_type` = ? '; $arguments[] = $shareType; } if ($owner !== null) { $where .= ' AND `uid_owner` = ? '; $arguments[] = $owner; } $query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where); $result = \OC_DB::executeAudited($query, $arguments); while ($row = $result->fetchRow()) { if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) { continue; } if ($fileDependent && (int)$row['file_parent'] === -1) { // if it is a mount point we need to get the path from the mount manager $mountManager = \OC\Files\Filesystem::getMountManager(); $mountPoint = $mountManager->findByStorageId($row['storage_id']); if (!empty($mountPoint)) { $path = $mountPoint[0]->getMountPoint(); $path = trim($path, '/'); $path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt` $row['path'] = $path; } else { \OC::$server->getLogger()->warning( 'Could not resolve mount point for ' . $row['storage_id'], ['app' => 'OCP\Share'] ); } } $shares[] = $row; } //if didn't found a result than let's look for a group share. if(empty($shares) && $user !== null) { $userObject = \OC::$server->getUserManager()->get($user); $groups = []; if ($userObject) { $groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject); } if (!empty($groups)) { $where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)'; $arguments = array($itemSource, $itemType, $groups); $types = array(null, null, IQueryBuilder::PARAM_STR_ARRAY); if ($owner !== null) { $where .= ' AND `uid_owner` = ?'; $arguments[] = $owner; $types[] = null; } // TODO: inject connection, hopefully one day in the future when this // class isn't static anymore... $conn = \OC::$server->getDatabaseConnection(); $result = $conn->executeQuery( 'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where, $arguments, $types ); while ($row = $result->fetch()) { $shares[] = $row; } } } return $shares; } /** * Get the item of item type shared with the current user by source * @param string $itemType * @param string $itemSource * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters * @param boolean $includeCollections * @param string $shareWith (optional) define against which user should be checked, default: current user * @return array */ public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false, $shareWith = null) { $shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith; return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format, $parameters, 1, $includeCollections, true); } /** * Get the item of item type shared by a link * @param string $itemType * @param string $itemSource * @param string $uidOwner Owner of link * @return array */ public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) { return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, null, 1); } /** * Based on the given token the share information will be returned - password protected shares will be verified * @param string $token * @param bool $checkPasswordProtection * @return array|boolean false will be returned in case the token is unknown or unauthorized */ public static function getShareByToken($token, $checkPasswordProtection = true) { $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1); $result = $query->execute(array($token)); if ($result === false) { \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage() . ', token=' . $token, \OCP\Util::ERROR); } $row = $result->fetchRow(); if ($row === false) { return false; } if (is_array($row) and self::expireItem($row)) { return false; } // password protected shares need to be authenticated if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) { return false; } return $row; } /** * resolves reshares down to the last real share * @param array $linkItem * @return array file owner */ public static function resolveReShare($linkItem) { if (isset($linkItem['parent'])) { $parent = $linkItem['parent']; while (isset($parent)) { $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1); $item = $query->execute(array($parent))->fetchRow(); if (isset($item['parent'])) { $parent = $item['parent']; } else { return $item; } } } return $linkItem; } /** * Get the shared items of item type owned by the current user * @param string $itemType * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters * @param int $limit Number of items to return (optional) Returns all by default * @param boolean $includeCollections * @return mixed Return depends on format */ public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format, $parameters, $limit, $includeCollections); } /** * Get the shared item of item type owned by the current user * @param string $itemType * @param string $itemSource * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters * @param boolean $includeCollections * @return mixed Return depends on format */ public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format, $parameters, -1, $includeCollections); } /** * Get all users an item is shared with * @param string $itemType * @param string $itemSource * @param string $uidOwner * @param boolean $includeCollections * @param boolean $checkExpireDate * @return array Return array of users */ public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) { $users = array(); $items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate); if ($items) { foreach ($items as $item) { if ((int)$item['share_type'] === self::SHARE_TYPE_USER) { $users[] = $item['share_with']; } else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) { $group = \OC::$server->getGroupManager()->get($item['share_with']); $userIds = []; if ($group) { $users = $group->searchUsers('', -1, 0); foreach ($users as $user) { $userIds[] = $user->getUID(); } return $userIds; } $users = array_merge($users, $userIds); } } } return $users; } /** * Share an item with a user, group, or via private link * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $shareWith User or group the item is being shared with * @param int $permissions CRUDS * @param string $itemSourceName * @param \DateTime $expirationDate * @param bool $passwordChanged * @return boolean|string Returns true on success or false on failure, Returns token on success for links * @throws \OC\HintException when the share type is remote and the shareWith is invalid * @throws \Exception */ public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) { $backend = self::getBackend($itemType); $l = \OC::$server->getL10N('lib'); if ($backend->isShareTypeAllowed($shareType) === false) { $message = 'Sharing %s failed, because the backend does not allow shares from type %i'; $message_t = $l->t('Sharing %s failed, because the backend does not allow shares from type %i', array($itemSourceName, $shareType)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareType), \OCP\Util::DEBUG); throw new \Exception($message_t); } $uidOwner = \OC_User::getUser(); $shareWithinGroupOnly = self::shareWithGroupMembersOnly(); if (is_null($itemSourceName)) { $itemSourceName = $itemSource; } $itemName = $itemSourceName; // check if file can be shared if ($itemType === 'file' or $itemType === 'folder') { $path = \OC\Files\Filesystem::getPath($itemSource); $itemName = $path; // verify that the file exists before we try to share it if (!$path) { $message = 'Sharing %s failed, because the file does not exist'; $message_t = $l->t('Sharing %s failed, because the file does not exist', array($itemSourceName)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG); throw new \Exception($message_t); } // verify that the user has share permission if (!\OC\Files\Filesystem::isSharable($path) || \OCP\Util::isSharingDisabledForUser()) { $message = 'You are not allowed to share %s'; $message_t = $l->t('You are not allowed to share %s', [$path]); \OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG); throw new \Exception($message_t); } } //verify that we don't share a folder which already contains a share mount point if ($itemType === 'folder') { $path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/'; $mountManager = \OC\Files\Filesystem::getMountManager(); $mounts = $mountManager->findIn($path); foreach ($mounts as $mount) { if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { $message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!'; \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG); throw new \Exception($message); } } } // single file shares should never have delete permissions if ($itemType === 'file') { $permissions = (int)$permissions & ~\OCP\Constants::PERMISSION_DELETE; } //Validate expirationDate if ($expirationDate !== null) { try { /* * Reuse the validateExpireDate. * We have to pass time() since the second arg is the time * the file was shared, since it is not shared yet we just use * the current time. */ $expirationDate = self::validateExpireDate($expirationDate->format('Y-m-d'), time(), $itemType, $itemSource); } catch (\Exception $e) { throw new \OC\HintException($e->getMessage(), $e->getMessage(), 404); } } // Verify share type and sharing conditions are met if ($shareType === self::SHARE_TYPE_USER) { if ($shareWith == $uidOwner) { $message = 'Sharing %s failed, because you can not share with yourself'; $message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG); throw new \Exception($message_t); } if (!\OC_User::userExists($shareWith)) { $message = 'Sharing %s failed, because the user %s does not exist'; $message_t = $l->t('Sharing %s failed, because the user %s does not exist', array($itemSourceName, $shareWith)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG); throw new \Exception($message_t); } if ($shareWithinGroupOnly) { $userManager = \OC::$server->getUserManager(); $groupManager = \OC::$server->getGroupManager(); $userOwner = $userManager->get($uidOwner); $userShareWith = $userManager->get($shareWith); $groupsOwner = []; $groupsShareWith = []; if ($userOwner) { $groupsOwner = $groupManager->getUserGroupIds($userOwner); } if ($userShareWith) { $groupsShareWith = $groupManager->getUserGroupIds($userShareWith); } $inGroup = array_intersect($groupsOwner, $groupsShareWith); if (empty($inGroup)) { $message = 'Sharing %s failed, because the user ' .'%s is not a member of any groups that %s is a member of'; $message_t = $l->t('Sharing %s failed, because the user %s is not a member of any groups that %s is a member of', array($itemName, $shareWith, $uidOwner)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemName, $shareWith, $uidOwner), \OCP\Util::DEBUG); throw new \Exception($message_t); } } // Check if the item source is already shared with the user, either from the same owner or a different user if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) { // Only allow the same share to occur again if it is the same // owner and is not a user share, this use case is for increasing // permissions for a specific user if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { $message = 'Sharing %s failed, because this item is already shared with %s'; $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG); throw new \Exception($message_t); } } if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_USER, $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) { // Only allow the same share to occur again if it is the same // owner and is not a user share, this use case is for increasing // permissions for a specific user if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { $message = 'Sharing %s failed, because this item is already shared with user %s'; $message_t = $l->t('Sharing %s failed, because this item is already shared with user %s', array($itemSourceName, $shareWith)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::ERROR); throw new \Exception($message_t); } } } else if ($shareType === self::SHARE_TYPE_GROUP) { if (!\OC::$server->getGroupManager()->groupExists($shareWith)) { $message = 'Sharing %s failed, because the group %s does not exist'; $message_t = $l->t('Sharing %s failed, because the group %s does not exist', array($itemSourceName, $shareWith)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG); throw new \Exception($message_t); } if ($shareWithinGroupOnly) { $group = \OC::$server->getGroupManager()->get($shareWith); $user = \OC::$server->getUserManager()->get($uidOwner); if (!$group || !$user || !$group->inGroup($user)) { $message = 'Sharing %s failed, because ' . '%s is not a member of the group %s'; $message_t = $l->t('Sharing %s failed, because %s is not a member of the group %s', array($itemSourceName, $uidOwner, $shareWith)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OCP\Util::DEBUG); throw new \Exception($message_t); } } // Check if the item source is already shared with the group, either from the same owner or a different user // The check for each user in the group is done inside the put() function if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) { if ($checkExists['share_with'] === $shareWith && $checkExists['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) { $message = 'Sharing %s failed, because this item is already shared with %s'; $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG); throw new \Exception($message_t); } } // Convert share with into an array with the keys group and users $group = $shareWith; $shareWith = array(); $shareWith['group'] = $group; $groupObject = \OC::$server->getGroupManager()->get($group); $userIds = []; if ($groupObject) { $users = $groupObject->searchUsers('', -1, 0); foreach ($users as $user) { $userIds[] = $user->getUID(); } } $shareWith['users'] = array_diff($userIds, array($uidOwner)); } else if ($shareType === self::SHARE_TYPE_LINK) { $updateExistingShare = false; if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') == 'yes') { // IF the password is changed via the old ajax endpoint verify it before deleting the old share if ($passwordChanged === true) { self::verifyPassword($shareWith); } // when updating a link share // FIXME Don't delete link if we update it if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, null, 1)) { // remember old token $oldToken = $checkExists['token']; $oldPermissions = $checkExists['permissions']; //delete the old share Helper::delete($checkExists['id']); $updateExistingShare = true; } if ($passwordChanged === null) { // Generate hash of password - same method as user passwords if (is_string($shareWith) && $shareWith !== '') { self::verifyPassword($shareWith); $shareWith = \OC::$server->getHasher()->hash($shareWith); } else { // reuse the already set password, but only if we change permissions // otherwise the user disabled the password protection if ($checkExists && (int)$permissions !== (int)$oldPermissions) { $shareWith = $checkExists['share_with']; } } } else { if ($passwordChanged === true) { if (is_string($shareWith) && $shareWith !== '') { self::verifyPassword($shareWith); $shareWith = \OC::$server->getHasher()->hash($shareWith); } } else if ($updateExistingShare) { $shareWith = $checkExists['share_with']; } } if (\OCP\Util::isPublicLinkPasswordRequired() && empty($shareWith)) { $message = 'You need to provide a password to create a public link, only protected links are allowed'; $message_t = $l->t('You need to provide a password to create a public link, only protected links are allowed'); \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG); throw new \Exception($message_t); } if ($updateExistingShare === false && self::isDefaultExpireDateEnabled() && empty($expirationDate)) { $expirationDate = Helper::calcExpireDate(); } // Generate token if (isset($oldToken)) { $token = $oldToken; } else { $token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_UPPER. \OCP\Security\ISecureRandom::CHAR_DIGITS ); } $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName, $expirationDate); if ($result) { return $token; } else { return false; } } $message = 'Sharing %s failed, because sharing with links is not allowed'; $message_t = $l->t('Sharing %s failed, because sharing with links is not allowed', array($itemSourceName)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG); throw new \Exception($message_t); } else if ($shareType === self::SHARE_TYPE_REMOTE) { /* * Check if file is not already shared with the remote user */ if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_REMOTE, $shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true, true)) { $message = 'Sharing %s failed, because this item is already shared with %s'; $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG); throw new \Exception($message_t); } // don't allow federated shares if source and target server are the same list($user, $remote) = Helper::splitUserRemote($shareWith); $currentServer = self::removeProtocolFromUrl(\OC::$server->getURLGenerator()->getAbsoluteURL('/')); $currentUser = \OC::$server->getUserSession()->getUser()->getUID(); if (Helper::isSameUserOnSameServer($user, $remote, $currentUser, $currentServer)) { $message = 'Not allowed to create a federated share with the same user.'; $message_t = $l->t('Not allowed to create a federated share with the same user'); \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG); throw new \Exception($message_t); } $token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER . \OCP\Security\ISecureRandom::CHAR_DIGITS); $shareWith = $user . '@' . $remote; $shareId = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName); $send = false; if ($shareId) { $send = self::sendRemoteShare($token, $shareWith, $itemSourceName, $shareId, $uidOwner); } if ($send === false) { $currentUser = \OC::$server->getUserSession()->getUser()->getUID(); self::unshare($itemType, $itemSource, $shareType, $shareWith, $currentUser); $message_t = $l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', array($itemSourceName, $shareWith)); throw new \Exception($message_t); } return $send; } else { // Future share types need to include their own conditions $message = 'Share type %s is not valid for %s'; $message_t = $l->t('Share type %s is not valid for %s', array($shareType, $itemSource)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $shareType, $itemSource), \OCP\Util::DEBUG); throw new \Exception($message_t); } // Put the item into the database $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName, $expirationDate); return $result ? true : false; } /** * Unshare an item from a user, group, or delete a private link * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $shareWith User or group the item is being shared with * @param string $owner owner of the share, if null the current user is used * @return boolean true on success or false on failure */ public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) { // check if it is a valid itemType self::getBackend($itemType); $items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType); $toDelete = array(); $newParent = null; $currentUser = $owner ? $owner : \OC_User::getUser(); foreach ($items as $item) { // delete the item with the expected share_type and owner if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) { $toDelete = $item; // if there is more then one result we don't have to delete the children // but update their parent. For group shares the new parent should always be // the original group share and not the db entry with the unique name } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) { $newParent = $item['parent']; } else { $newParent = $item['id']; } } if (!empty($toDelete)) { self::unshareItem($toDelete, $newParent); return true; } return false; } /** * Unshare an item from all users, groups, and remove all links * @param string $itemType * @param string $itemSource * @return boolean true on success or false on failure */ public static function unshareAll($itemType, $itemSource) { // Get all of the owners of shares of this item. $query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' ); $result = $query->execute(array($itemType, $itemSource)); $shares = array(); // Add each owner's shares to the array of all shares for this item. while ($row = $result->fetchRow()) { $shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner'])); } if (!empty($shares)) { // Pass all the vars we have for now, they may be useful $hookParams = array( 'itemType' => $itemType, 'itemSource' => $itemSource, 'shares' => $shares, ); \OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams); foreach ($shares as $share) { self::unshareItem($share); } \OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams); return true; } return false; } /** * Unshare an item shared with the current user * @param string $itemType * @param string $itemOrigin Item target or source * @param boolean $originIsSource true if $itemOrigin is the source, false if $itemOrigin is the target (optional) * @return boolean true on success or false on failure * * Unsharing from self is not allowed for items inside collections */ public static function unshareFromSelf($itemType, $itemOrigin, $originIsSource = false) { $originType = ($originIsSource) ? 'source' : 'target'; $uid = \OCP\User::getUser(); if ($itemType === 'file' || $itemType === 'folder') { $statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_' . $originType . '` = ?'; } else { $statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_' . $originType . '` = ?'; } $query = \OCP\DB::prepare($statement); $result = $query->execute(array($itemType, $itemOrigin)); $shares = $result->fetchAll(); $listOfUnsharedItems = array(); $itemUnshared = false; foreach ($shares as $share) { if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER && $share['share_with'] === $uid) { $deletedShares = Helper::delete($share['id']); $shareTmp = array( 'id' => $share['id'], 'shareWith' => $share['share_with'], 'itemTarget' => $share['item_target'], 'itemType' => $share['item_type'], 'shareType' => (int)$share['share_type'], ); if (isset($share['file_target'])) { $shareTmp['fileTarget'] = $share['file_target']; } $listOfUnsharedItems = array_merge($listOfUnsharedItems, $deletedShares, array($shareTmp)); $itemUnshared = true; break; } elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) { $group = \OC::$server->getGroupManager()->get($share['share_with']); $user = \OC::$server->getUserManager()->get($uid); if ($group && $user && $group->inGroup($user)) { $groupShare = $share; } } elseif ((int)$share['share_type'] === self::$shareTypeGroupUserUnique && $share['share_with'] === $uid) { $uniqueGroupShare = $share; } } if (!$itemUnshared && isset($groupShare) && !isset($uniqueGroupShare)) { $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`' .' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,' .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)' .' VALUES (?,?,?,?,?,?,?,?,?,?,?)'); $query->execute(array($groupShare['item_type'], $groupShare['item_source'], $groupShare['item_target'], $groupShare['id'], self::$shareTypeGroupUserUnique, \OC_User::getUser(), $groupShare['uid_owner'], 0, $groupShare['stime'], $groupShare['file_source'], $groupShare['file_target'])); $shareTmp = array( 'id' => $groupShare['id'], 'shareWith' => $groupShare['share_with'], 'itemTarget' => $groupShare['item_target'], 'itemType' => $groupShare['item_type'], 'shareType' => (int)$groupShare['share_type'], ); if (isset($groupShare['file_target'])) { $shareTmp['fileTarget'] = $groupShare['file_target']; } $listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]); $itemUnshared = true; } elseif (!$itemUnshared && isset($uniqueGroupShare)) { $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); $query->execute(array(0, $uniqueGroupShare['id'])); $shareTmp = array( 'id' => $uniqueGroupShare['id'], 'shareWith' => $uniqueGroupShare['share_with'], 'itemTarget' => $uniqueGroupShare['item_target'], 'itemType' => $uniqueGroupShare['item_type'], 'shareType' => (int)$uniqueGroupShare['share_type'], ); if (isset($uniqueGroupShare['file_target'])) { $shareTmp['fileTarget'] = $uniqueGroupShare['file_target']; } $listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]); $itemUnshared = true; } if ($itemUnshared) { \OC_Hook::emit('OCP\Share', 'post_unshareFromSelf', array('unsharedItems' => $listOfUnsharedItems, 'itemType' => $itemType)); } return $itemUnshared; } /** * sent status if users got informed by mail about share * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $recipient with whom was the file shared * @param boolean $status */ public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) { $status = $status ? 1 : 0; $query = \OC_DB::prepare( 'UPDATE `*PREFIX*share` SET `mail_send` = ? WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ? AND `share_with` = ?'); $result = $query->execute(array($status, $itemType, $itemSource, $shareType, $recipient)); if($result === false) { \OCP\Util::writeLog('OCP\Share', 'Couldn\'t set send mail status', \OCP\Util::ERROR); } } /** * Set the permissions of an item for a specific user or group * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $shareWith User or group the item is being shared with * @param int $permissions CRUDS permissions * @return boolean true on success or false on failure * @throws \Exception when trying to grant more permissions then the user has himself */ public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) { $l = \OC::$server->getL10N('lib'); $connection = \OC::$server->getDatabaseConnection(); $intArrayToLiteralArray = function($intArray, $eb) { return array_map(function($int) use ($eb) { return $eb->literal((int)$int, 'integer'); }, $intArray); }; $sanitizeItem = function($item) { $item['id'] = (int)$item['id']; $item['premissions'] = (int)$item['permissions']; return $item; }; if ($rootItem = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) { // Check if this item is a reshare and verify that the permissions // granted don't exceed the parent shared item if (isset($rootItem['parent'])) { $qb = $connection->getQueryBuilder(); $qb->select('permissions') ->from('share') ->where($qb->expr()->eq('id', $qb->createParameter('id'))) ->setParameter(':id', $rootItem['parent']); $dbresult = $qb->execute(); $result = $dbresult->fetch(); $dbresult->closeCursor(); if (~(int)$result['permissions'] & $permissions) { $message = 'Setting permissions for %s failed,' .' because the permissions exceed permissions granted to %s'; $message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', array($itemSource, \OC_User::getUser())); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, \OC_User::getUser()), \OCP\Util::DEBUG); throw new \Exception($message_t); } } $qb = $connection->getQueryBuilder(); $qb->update('share') ->set('permissions', $qb->createParameter('permissions')) ->where($qb->expr()->eq('id', $qb->createParameter('id'))) ->setParameter(':id', $rootItem['id']) ->setParameter(':permissions', $permissions); $qb->execute(); if ($itemType === 'file' || $itemType === 'folder') { \OC_Hook::emit('OCP\Share', 'post_update_permissions', array( 'itemType' => $itemType, 'itemSource' => $itemSource, 'shareType' => $shareType, 'shareWith' => $shareWith, 'uidOwner' => \OC_User::getUser(), 'permissions' => $permissions, 'path' => $rootItem['path'], 'share' => $rootItem )); } // Share id's to update with the new permissions $ids = []; $items = []; // Check if permissions were removed if ((int)$rootItem['permissions'] & ~$permissions) { // If share permission is removed all reshares must be deleted if (($rootItem['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) { // delete all shares, keep parent and group children Helper::delete($rootItem['id'], true, null, null, true); } // Remove permission from all children $parents = [$rootItem['id']]; while (!empty($parents)) { $parents = $intArrayToLiteralArray($parents, $qb->expr()); $qb = $connection->getQueryBuilder(); $qb->select('id', 'permissions', 'item_type') ->from('share') ->where($qb->expr()->in('parent', $parents)); $result = $qb->execute(); // Reset parents array, only go through loop again if // items are found that need permissions removed $parents = []; while ($item = $result->fetch()) { $item = $sanitizeItem($item); $items[] = $item; // Check if permissions need to be removed if ($item['permissions'] & ~$permissions) { // Add to list of items that need permissions removed $ids[] = $item['id']; $parents[] = $item['id']; } } $result->closeCursor(); } // Remove the permissions for all reshares of this item if (!empty($ids)) { $ids = "'".implode("','", $ids)."'"; // TODO this should be done with Doctrine platform objects if (\OC::$server->getConfig()->getSystemValue("dbtype") === 'oci') { $andOp = 'BITAND(`permissions`, ?)'; } else { $andOp = '`permissions` & ?'; } $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp .' WHERE `id` IN ('.$ids.')'); $query->execute(array($permissions)); } } /* * Permissions were added * Update all USERGROUP shares. (So group shares where the user moved their mountpoint). */ if ($permissions & ~(int)$rootItem['permissions']) { $qb = $connection->getQueryBuilder(); $qb->select('id', 'permissions', 'item_type') ->from('share') ->where($qb->expr()->eq('parent', $qb->createParameter('parent'))) ->andWhere($qb->expr()->eq('share_type', $qb->createParameter('share_type'))) ->andWhere($qb->expr()->neq('permissions', $qb->createParameter('shareDeleted'))) ->setParameter(':parent', (int)$rootItem['id']) ->setParameter(':share_type', 2) ->setParameter(':shareDeleted', 0); $result = $qb->execute(); $ids = []; while ($item = $result->fetch()) { $item = $sanitizeItem($item); $items[] = $item; $ids[] = $item['id']; } $result->closeCursor(); // Add permssions for all USERGROUP shares of this item if (!empty($ids)) { $ids = $intArrayToLiteralArray($ids, $qb->expr()); $qb = $connection->getQueryBuilder(); $qb->update('share') ->set('permissions', $qb->createParameter('permissions')) ->where($qb->expr()->in('id', $ids)) ->setParameter(':permissions', $permissions); $qb->execute(); } } foreach ($items as $item) { \OC_Hook::emit('OCP\Share', 'post_update_permissions', ['share' => $item]); } return true; } $message = 'Setting permissions for %s failed, because the item was not found'; $message_t = $l->t('Setting permissions for %s failed, because the item was not found', array($itemSource)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG); throw new \Exception($message_t); } /** * validate expiration date if it meets all constraints * * @param string $expireDate well formatted date string, e.g. "DD-MM-YYYY" * @param string $shareTime timestamp when the file was shared * @param string $itemType * @param string $itemSource * @return \DateTime validated date * @throws \Exception when the expire date is in the past or further in the future then the enforced date */ private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) { $l = \OC::$server->getL10N('lib'); $date = new \DateTime($expireDate); $today = new \DateTime('now'); // if the user doesn't provide a share time we need to get it from the database // fall-back mode to keep API stable, because the $shareTime parameter was added later $defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced(); if ($defaultExpireDateEnforced && $shareTime === null) { $items = self::getItemShared($itemType, $itemSource); $firstItem = reset($items); $shareTime = (int)$firstItem['stime']; } if ($defaultExpireDateEnforced) { // initialize max date with share time $maxDate = new \DateTime(); $maxDate->setTimestamp($shareTime); $maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7'); $maxDate->add(new \DateInterval('P' . $maxDays . 'D')); if ($date > $maxDate) { $warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared'; $warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', array($maxDays)); \OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN); throw new \Exception($warning_t); } } if ($date < $today) { $message = 'Cannot set expiration date. Expiration date is in the past'; $message_t = $l->t('Cannot set expiration date. Expiration date is in the past'); \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN); throw new \Exception($message_t); } return $date; } /** * Set expiration date for a share * @param string $itemType * @param string $itemSource * @param string $date expiration date * @param int $shareTime timestamp from when the file was shared * @return boolean * @throws \Exception when the expire date is not set, in the past or further in the future then the enforced date */ public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) { $user = \OC_User::getUser(); $l = \OC::$server->getL10N('lib'); if ($date == '') { if (\OCP\Util::isDefaultExpireDateEnforced()) { $warning = 'Cannot clear expiration date. Shares are required to have an expiration date.'; $warning_t = $l->t('Cannot clear expiration date. Shares are required to have an expiration date.'); \OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN); throw new \Exception($warning_t); } else { $date = null; } } else { $date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource); } $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ? AND `uid_owner` = ? AND `share_type` = ?'); $query->bindValue(1, $date, 'datetime'); $query->bindValue(2, $itemType); $query->bindValue(3, $itemSource); $query->bindValue(4, $user); $query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK); $query->execute(); \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array( 'itemType' => $itemType, 'itemSource' => $itemSource, 'date' => $date, 'uidOwner' => $user )); return true; } /** * Retrieve the owner of a connection * * @param IDBConnection $connection * @param int $shareId * @throws \Exception * @return string uid of share owner */ private static function getShareOwner(IDBConnection $connection, $shareId) { $qb = $connection->getQueryBuilder(); $qb->select('uid_owner') ->from('share') ->where($qb->expr()->eq('id', $qb->createParameter('shareId'))) ->setParameter(':shareId', $shareId); $dbResult = $qb->execute(); $result = $dbResult->fetch(); $dbResult->closeCursor(); if (empty($result)) { throw new \Exception('Share not found'); } return $result['uid_owner']; } /** * Set password for a public link share * * @param IUserSession $userSession * @param IDBConnection $connection * @param IConfig $config * @param int $shareId * @param string $password * @throws \Exception * @return boolean */ public static function setPassword(IUserSession $userSession, IDBConnection $connection, IConfig $config, $shareId, $password) { $user = $userSession->getUser(); if (is_null($user)) { throw new \Exception("User not logged in"); } $uid = self::getShareOwner($connection, $shareId); if ($uid !== $user->getUID()) { throw new \Exception('Cannot update share of a different user'); } if ($password === '') { $password = null; } //If passwords are enforced the password can't be null if (self::enforcePassword($config) && is_null($password)) { throw new \Exception('Cannot remove password'); } self::verifyPassword($password); $qb = $connection->getQueryBuilder(); $qb->update('share') ->set('share_with', $qb->createParameter('pass')) ->where($qb->expr()->eq('id', $qb->createParameter('shareId'))) ->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password)) ->setParameter(':shareId', $shareId); $qb->execute(); return true; } /** * Checks whether a share has expired, calls unshareItem() if yes. * @param array $item Share data (usually database row) * @return boolean True if item was expired, false otherwise. */ protected static function expireItem(array $item) { $result = false; // only use default expiration date for link shares if ((int) $item['share_type'] === self::SHARE_TYPE_LINK) { // calculate expiration date if (!empty($item['expiration'])) { $userDefinedExpire = new \DateTime($item['expiration']); $expires = $userDefinedExpire->getTimestamp(); } else { $expires = null; } // get default expiration settings $defaultSettings = Helper::getDefaultExpireSetting(); $expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires); if (is_int($expires)) { $now = time(); if ($now > $expires) { self::unshareItem($item); $result = true; } } } return $result; } /** * Unshares a share given a share data array * @param array $item Share data (usually database row) * @param int $newParent parent ID * @return null */ protected static function unshareItem(array $item, $newParent = null) { $shareType = (int)$item['share_type']; $shareWith = null; if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) { $shareWith = $item['share_with']; } // Pass all the vars we have for now, they may be useful $hookParams = array( 'id' => $item['id'], 'itemType' => $item['item_type'], 'itemSource' => $item['item_source'], 'shareType' => $shareType, 'shareWith' => $shareWith, 'itemParent' => $item['parent'], 'uidOwner' => $item['uid_owner'], ); if($item['item_type'] === 'file' || $item['item_type'] === 'folder') { $hookParams['fileSource'] = $item['file_source']; $hookParams['fileTarget'] = $item['file_target']; } \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams); $deletedShares = Helper::delete($item['id'], false, null, $newParent); $deletedShares[] = $hookParams; $hookParams['deletedShares'] = $deletedShares; \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams); if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) { list(, $remote) = Helper::splitUserRemote($item['share_with']); self::sendRemoteUnshare($remote, $item['id'], $item['token']); } } /** * Get the backend class for the specified item type * @param string $itemType * @throws \Exception * @return \OCP\Share_Backend */ public static function getBackend($itemType) { $l = \OC::$server->getL10N('lib'); if (isset(self::$backends[$itemType])) { return self::$backends[$itemType]; } else if (isset(self::$backendTypes[$itemType]['class'])) { $class = self::$backendTypes[$itemType]['class']; if (class_exists($class)) { self::$backends[$itemType] = new $class; if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) { $message = 'Sharing backend %s must implement the interface OCP\Share_Backend'; $message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', array($class)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR); throw new \Exception($message_t); } return self::$backends[$itemType]; } else { $message = 'Sharing backend %s not found'; $message_t = $l->t('Sharing backend %s not found', array($class)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR); throw new \Exception($message_t); } } $message = 'Sharing backend for %s not found'; $message_t = $l->t('Sharing backend for %s not found', array($itemType)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), \OCP\Util::ERROR); throw new \Exception($message_t); } /** * Check if resharing is allowed * @return boolean true if allowed or false * * Resharing is allowed by default if not configured */ public static function isResharingAllowed() { if (!isset(self::$isResharingAllowed)) { if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') { self::$isResharingAllowed = true; } else { self::$isResharingAllowed = false; } } return self::$isResharingAllowed; } /** * Get a list of collection item types for the specified item type * @param string $itemType * @return array */ private static function getCollectionItemTypes($itemType) { $collectionTypes = array($itemType); foreach (self::$backendTypes as $type => $backend) { if (in_array($backend['collectionOf'], $collectionTypes)) { $collectionTypes[] = $type; } } // TODO Add option for collections to be collection of themselves, only 'folder' does it now... if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) { unset($collectionTypes[0]); } // Return array if collections were found or the item type is a // collection itself - collections can be inside collections if (count($collectionTypes) > 0) { return $collectionTypes; } return false; } /** * Get the owners of items shared with a user. * * @param string $user The user the items are shared with. * @param string $type The type of the items shared with the user. * @param boolean $includeCollections Include collection item types (optional) * @param boolean $includeOwner include owner in the list of users the item is shared with (optional) * @return array */ public static function getSharedItemsOwners($user, $type, $includeCollections = false, $includeOwner = false) { // First, we find out if $type is part of a collection (and if that collection is part of // another one and so on). $collectionTypes = array(); if (!$includeCollections || !$collectionTypes = self::getCollectionItemTypes($type)) { $collectionTypes[] = $type; } // Of these collection types, along with our original $type, we make a // list of the ones for which a sharing backend has been registered. // FIXME: Ideally, we wouldn't need to nest getItemsSharedWith in this loop but just call it // with its $includeCollections parameter set to true. Unfortunately, this fails currently. $allMaybeSharedItems = array(); foreach ($collectionTypes as $collectionType) { if (isset(self::$backends[$collectionType])) { $allMaybeSharedItems[$collectionType] = self::getItemsSharedWithUser( $collectionType, $user, self::FORMAT_NONE ); } } $owners = array(); if ($includeOwner) { $owners[] = $user; } // We take a look at all shared items of the given $type (or of the collections it is part of) // and find out their owners. Then, we gather the tags for the original $type from all owners, // and return them as elements of a list that look like "Tag (owner)". foreach ($allMaybeSharedItems as $collectionType => $maybeSharedItems) { foreach ($maybeSharedItems as $sharedItem) { if (isset($sharedItem['id'])) { //workaround for https://github.com/owncloud/core/issues/2814 $owners[] = $sharedItem['uid_owner']; } } } return $owners; } /** * Get shared items from the database * @param string $itemType * @param string $item Item source or target (optional) * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique * @param string $shareWith User or group the item is being shared with * @param string $uidOwner User that is the owner of shared items (optional) * @param int $format Format to convert items to with formatItems() (optional) * @param mixed $parameters to pass to formatItems() (optional) * @param int $limit Number of items to return, -1 to return all matches (optional) * @param boolean $includeCollections Include collection item types (optional) * @param boolean $itemShareWithBySource (optional) * @param boolean $checkExpireDate * @return array * * See public functions getItem(s)... for parameter usage * */ public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null, $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate = true) { if (!self::isEnabled()) { return array(); } $backend = self::getBackend($itemType); $collectionTypes = false; // Get filesystem root to add it to the file target and remove from the // file source, match file_source with the file cache if ($itemType == 'file' || $itemType == 'folder') { if(!is_null($uidOwner)) { $root = \OC\Files\Filesystem::getRoot(); } else { $root = ''; } $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` '; if (!isset($item)) { $where .= ' AND `file_target` IS NOT NULL '; } $where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` '; $fileDependent = true; $queryArgs = array(); } else { $fileDependent = false; $root = ''; $collectionTypes = self::getCollectionItemTypes($itemType); if ($includeCollections && !isset($item) && $collectionTypes) { // If includeCollections is true, find collections of this item type, e.g. a music album contains songs if (!in_array($itemType, $collectionTypes)) { $itemTypes = array_merge(array($itemType), $collectionTypes); } else { $itemTypes = $collectionTypes; } $placeholders = join(',', array_fill(0, count($itemTypes), '?')); $where = ' WHERE `item_type` IN ('.$placeholders.'))'; $queryArgs = $itemTypes; } else { $where = ' WHERE `item_type` = ?'; $queryArgs = array($itemType); } } if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { $where .= ' AND `share_type` != ?'; $queryArgs[] = self::SHARE_TYPE_LINK; } if (isset($shareType)) { // Include all user and group items if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) { $where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) '; $queryArgs[] = self::SHARE_TYPE_USER; $queryArgs[] = self::$shareTypeGroupUserUnique; $queryArgs[] = $shareWith; $user = \OC::$server->getUserManager()->get($shareWith); $groups = []; if ($user) { $groups = \OC::$server->getGroupManager()->getUserGroupIds($user); } if (!empty($groups)) { $placeholders = join(',', array_fill(0, count($groups), '?')); $where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) '; $queryArgs[] = self::SHARE_TYPE_GROUP; $queryArgs = array_merge($queryArgs, $groups); } $where .= ')'; // Don't include own group shares $where .= ' AND `uid_owner` != ?'; $queryArgs[] = $shareWith; } else { $where .= ' AND `share_type` = ?'; $queryArgs[] = $shareType; if (isset($shareWith)) { $where .= ' AND `share_with` = ?'; $queryArgs[] = $shareWith; } } } if (isset($uidOwner)) { $where .= ' AND `uid_owner` = ?'; $queryArgs[] = $uidOwner; if (!isset($shareType)) { // Prevent unique user targets for group shares from being selected $where .= ' AND `share_type` != ?'; $queryArgs[] = self::$shareTypeGroupUserUnique; } if ($fileDependent) { $column = 'file_source'; } else { $column = 'item_source'; } } else { if ($fileDependent) { $column = 'file_target'; } else { $column = 'item_target'; } } if (isset($item)) { $collectionTypes = self::getCollectionItemTypes($itemType); if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) { $where .= ' AND ('; } else { $where .= ' AND'; } // If looking for own shared items, check item_source else check item_target if (isset($uidOwner) || $itemShareWithBySource) { // If item type is a file, file source needs to be checked in case the item was converted if ($fileDependent) { $where .= ' `file_source` = ?'; $column = 'file_source'; } else { $where .= ' `item_source` = ?'; $column = 'item_source'; } } else { if ($fileDependent) { $where .= ' `file_target` = ?'; $item = \OC\Files\Filesystem::normalizePath($item); } else { $where .= ' `item_target` = ?'; } } $queryArgs[] = $item; if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) { $placeholders = join(',', array_fill(0, count($collectionTypes), '?')); $where .= ' OR `item_type` IN ('.$placeholders.'))'; $queryArgs = array_merge($queryArgs, $collectionTypes); } } if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) { // Make sure the unique user target is returned if it exists, // unique targets should follow the group share in the database // If the limit is not 1, the filtering can be done later $where .= ' ORDER BY `*PREFIX*share`.`id` DESC'; } else { $where .= ' ORDER BY `*PREFIX*share`.`id` ASC'; } if ($limit != -1 && !$includeCollections) { // The limit must be at least 3, because filtering needs to be done if ($limit < 3) { $queryLimit = 3; } else { $queryLimit = $limit; } } else { $queryLimit = null; } $select = self::createSelectStatement($format, $fileDependent, $uidOwner); $root = strlen($root); $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit); $result = $query->execute($queryArgs); if ($result === false) { \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage() . ', select=' . $select . ' where=', \OCP\Util::ERROR); } $items = array(); $targets = array(); $switchedItems = array(); $mounts = array(); while ($row = $result->fetchRow()) { self::transformDBResults($row); // Filter out duplicate group shares for users with unique targets if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) { continue; } if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) { $row['share_type'] = self::SHARE_TYPE_GROUP; $row['unique_name'] = true; // remember that we use a unique name for this user $row['share_with'] = $items[$row['parent']]['share_with']; // if the group share was unshared from the user we keep the permission, otherwise // we take the permission from the parent because this is always the up-to-date // permission for the group share if ($row['permissions'] > 0) { $row['permissions'] = $items[$row['parent']]['permissions']; } // Remove the parent group share unset($items[$row['parent']]); if ($row['permissions'] == 0) { continue; } } else if (!isset($uidOwner)) { // Check if the same target already exists if (isset($targets[$row['id']])) { // Check if the same owner shared with the user twice // through a group and user share - this is allowed $id = $targets[$row['id']]; if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) { // Switch to group share type to ensure resharing conditions aren't bypassed if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) { $items[$id]['share_type'] = self::SHARE_TYPE_GROUP; $items[$id]['share_with'] = $row['share_with']; } // Switch ids if sharing permission is granted on only // one share to ensure correct parent is used if resharing if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE && (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) { $items[$row['id']] = $items[$id]; $switchedItems[$id] = $row['id']; unset($items[$id]); $id = $row['id']; } $items[$id]['permissions'] |= (int)$row['permissions']; } continue; } elseif (!empty($row['parent'])) { $targets[$row['parent']] = $row['id']; } } // Remove root from file source paths if retrieving own shared items if (isset($uidOwner) && isset($row['path'])) { if (isset($row['parent'])) { $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?'); $parentResult = $query->execute(array($row['parent'])); if ($result === false) { \OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' . \OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where, \OCP\Util::ERROR); } else { $parentRow = $parentResult->fetchRow(); $tmpPath = $parentRow['file_target']; // find the right position where the row path continues from the target path $pos = strrpos($row['path'], $parentRow['file_target']); $subPath = substr($row['path'], $pos); $splitPath = explode('/', $subPath); foreach (array_slice($splitPath, 2) as $pathPart) { $tmpPath = $tmpPath . '/' . $pathPart; } $row['path'] = $tmpPath; } } else { if (!isset($mounts[$row['storage']])) { $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']); if (is_array($mountPoints) && !empty($mountPoints)) { $mounts[$row['storage']] = current($mountPoints); } } if (!empty($mounts[$row['storage']])) { $path = $mounts[$row['storage']]->getMountPoint().$row['path']; $relPath = substr($path, $root); // path relative to data/user $row['path'] = rtrim($relPath, '/'); } } } if($checkExpireDate) { if (self::expireItem($row)) { continue; } } // Check if resharing is allowed, if not remove share permission if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) { $row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE; } // Add display names to result $row['share_with_displayname'] = $row['share_with']; if ( isset($row['share_with']) && $row['share_with'] != '' && $row['share_type'] === self::SHARE_TYPE_USER) { $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']); } else if(isset($row['share_with']) && $row['share_with'] != '' && $row['share_type'] === self::SHARE_TYPE_REMOTE) { $addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']); foreach ($addressBookEntries as $entry) { foreach ($entry['CLOUD'] as $cloudID) { if ($cloudID === $row['share_with']) { $row['share_with_displayname'] = $entry['FN']; } } } } if ( isset($row['uid_owner']) && $row['uid_owner'] != '') { $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']); } if ($row['permissions'] > 0) { $items[$row['id']] = $row; } } // group items if we are looking for items shared with the current user if (isset($shareWith) && $shareWith === \OCP\User::getUser()) { $items = self::groupItems($items, $itemType); } if (!empty($items)) { $collectionItems = array(); foreach ($items as &$row) { // Return only the item instead of a 2-dimensional array if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) { if ($format == self::FORMAT_NONE) { return $row; } else { break; } } // Check if this is a collection of the requested item type if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) { if (($collectionBackend = self::getBackend($row['item_type'])) && $collectionBackend instanceof \OCP\Share_Backend_Collection) { // Collections can be inside collections, check if the item is a collection if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) { $collectionItems[] = $row; } else { $collection = array(); $collection['item_type'] = $row['item_type']; if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { $collection['path'] = basename($row['path']); } $row['collection'] = $collection; // Fetch all of the children sources $children = $collectionBackend->getChildren($row[$column]); foreach ($children as $child) { $childItem = $row; $childItem['item_type'] = $itemType; if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') { $childItem['item_source'] = $child['source']; $childItem['item_target'] = $child['target']; } if ($backend instanceof \OCP\Share_Backend_File_Dependent) { if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { $childItem['file_source'] = $child['source']; } else { // TODO is this really needed if we already know that we use the file backend? $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']); $childItem['file_source'] = $meta['fileid']; } $childItem['file_target'] = \OC\Files\Filesystem::normalizePath($child['file_path']); } if (isset($item)) { if ($childItem[$column] == $item) { // Return only the item instead of a 2-dimensional array if ($limit == 1) { if ($format == self::FORMAT_NONE) { return $childItem; } else { // Unset the items array and break out of both loops $items = array(); $items[] = $childItem; break 2; } } else { $collectionItems[] = $childItem; } } } else { $collectionItems[] = $childItem; } } } } // Remove collection item $toRemove = $row['id']; if (array_key_exists($toRemove, $switchedItems)) { $toRemove = $switchedItems[$toRemove]; } unset($items[$toRemove]); } elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) { // FIXME: Thats a dirty hack to improve file sharing performance, // see github issue #10588 for more details // Need to find a solution which works for all back-ends $collectionBackend = self::getBackend($row['item_type']); $sharedParents = $collectionBackend->getParents($row['item_source']); foreach ($sharedParents as $parent) { $collectionItems[] = $parent; } } } if (!empty($collectionItems)) { $collectionItems = array_unique($collectionItems, SORT_REGULAR); $items = array_merge($items, $collectionItems); } // filter out invalid items, these can appear when subshare entries exist // for a group in which the requested user isn't a member any more $items = array_filter($items, function($item) { return $item['share_type'] !== self::$shareTypeGroupUserUnique; }); return self::formatResult($items, $column, $backend, $format, $parameters); } elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) { // FIXME: Thats a dirty hack to improve file sharing performance, // see github issue #10588 for more details // Need to find a solution which works for all back-ends $collectionItems = array(); $collectionBackend = self::getBackend('folder'); $sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner); foreach ($sharedParents as $parent) { $collectionItems[] = $parent; } if ($limit === 1) { return reset($collectionItems); } return self::formatResult($collectionItems, $column, $backend, $format, $parameters); } return array(); } /** * group items with link to the same source * * @param array $items * @param string $itemType * @return array of grouped items */ protected static function groupItems($items, $itemType) { $fileSharing = ($itemType === 'file' || $itemType === 'folder') ? true : false; $result = array(); foreach ($items as $item) { $grouped = false; foreach ($result as $key => $r) { // for file/folder shares we need to compare file_source, otherwise we compare item_source // only group shares if they already point to the same target, otherwise the file where shared // before grouping of shares was added. In this case we don't group them toi avoid confusions if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) || (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) { // add the first item to the list of grouped shares if (!isset($result[$key]['grouped'])) { $result[$key]['grouped'][] = $result[$key]; } $result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions']; $result[$key]['grouped'][] = $item; $grouped = true; break; } } if (!$grouped) { $result[] = $item; } } return $result; } /** * Put shared item into the database * @param string $itemType Item type * @param string $itemSource Item source * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $shareWith User or group the item is being shared with * @param string $uidOwner User that is the owner of shared item * @param int $permissions CRUDS permissions * @param boolean|array $parentFolder Parent folder target (optional) * @param string $token (optional) * @param string $itemSourceName name of the source item (optional) * @param \DateTime $expirationDate (optional) * @throws \Exception * @return mixed id of the new share or false */ private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) { $queriesToExecute = array(); $suggestedItemTarget = null; $groupFileTarget = $fileTarget = $suggestedFileTarget = $filePath = ''; $groupItemTarget = $itemTarget = $fileSource = $parent = 0; $result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate); if(!empty($result)) { $parent = $result['parent']; $itemSource = $result['itemSource']; $fileSource = $result['fileSource']; $suggestedItemTarget = $result['suggestedItemTarget']; $suggestedFileTarget = $result['suggestedFileTarget']; $filePath = $result['filePath']; } $isGroupShare = false; if ($shareType == self::SHARE_TYPE_GROUP) { $isGroupShare = true; if (isset($shareWith['users'])) { $users = $shareWith['users']; } else { $group = \OC::$server->getGroupManager()->get($shareWith['group']); if ($group) { $users = $group->searchUsers('', -1, 0); $userIds = []; foreach ($users as $user) { $userIds[] = $user->getUID(); } $users = $userIds; } else { $users = []; } } // remove current user from list if (in_array(\OCP\User::getUser(), $users)) { unset($users[array_search(\OCP\User::getUser(), $users)]); } $groupItemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget); $groupFileTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], $uidOwner, $filePath); // add group share to table and remember the id as parent $queriesToExecute['groupShare'] = array( 'itemType' => $itemType, 'itemSource' => $itemSource, 'itemTarget' => $groupItemTarget, 'shareType' => $shareType, 'shareWith' => $shareWith['group'], 'uidOwner' => $uidOwner, 'permissions' => $permissions, 'shareTime' => time(), 'fileSource' => $fileSource, 'fileTarget' => $groupFileTarget, 'token' => $token, 'parent' => $parent, 'expiration' => $expirationDate, ); } else { $users = array($shareWith); $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $suggestedItemTarget); } $run = true; $error = ''; $preHookData = array( 'itemType' => $itemType, 'itemSource' => $itemSource, 'shareType' => $shareType, 'uidOwner' => $uidOwner, 'permissions' => $permissions, 'fileSource' => $fileSource, 'expiration' => $expirationDate, 'token' => $token, 'run' => &$run, 'error' => &$error ); $preHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget; $preHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith; \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData); if ($run === false) { throw new \Exception($error); } foreach ($users as $user) { $sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource; $sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user); $userShareType = ($isGroupShare) ? self::$shareTypeGroupUserUnique : $shareType; if ($sourceExists && $sourceExists['item_source'] === $itemSource) { $fileTarget = $sourceExists['file_target']; $itemTarget = $sourceExists['item_target']; // for group shares we don't need a additional entry if the target is the same if($isGroupShare && $groupItemTarget === $itemTarget) { continue; } } elseif(!$sourceExists && !$isGroupShare) { $itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user, $uidOwner, $suggestedItemTarget, $parent); if (isset($fileSource)) { if ($parentFolder) { if ($parentFolder === true) { $fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user, $uidOwner, $suggestedFileTarget, $parent); if ($fileTarget != $groupFileTarget) { $parentFolders[$user]['folder'] = $fileTarget; } } else if (isset($parentFolder[$user])) { $fileTarget = $parentFolder[$user]['folder'].$itemSource; $parent = $parentFolder[$user]['id']; } } else { $fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user, $uidOwner, $suggestedFileTarget, $parent); } } else { $fileTarget = null; } } else { // group share which doesn't exists until now, check if we need a unique target for this user $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user, $uidOwner, $suggestedItemTarget, $parent); // do we also need a file target if (isset($fileSource)) { $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user, $uidOwner, $suggestedFileTarget, $parent); } else { $fileTarget = null; } if (($itemTarget === $groupItemTarget) && (!isset($fileSource) || $fileTarget === $groupFileTarget)) { continue; } } $queriesToExecute[] = array( 'itemType' => $itemType, 'itemSource' => $itemSource, 'itemTarget' => $itemTarget, 'shareType' => $userShareType, 'shareWith' => $user, 'uidOwner' => $uidOwner, 'permissions' => $permissions, 'shareTime' => time(), 'fileSource' => $fileSource, 'fileTarget' => $fileTarget, 'token' => $token, 'parent' => $parent, 'expiration' => $expirationDate, ); } $id = false; if ($isGroupShare) { $id = self::insertShare($queriesToExecute['groupShare']); // Save this id, any extra rows for this group share will need to reference it $parent = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share'); unset($queriesToExecute['groupShare']); } foreach ($queriesToExecute as $shareQuery) { $shareQuery['parent'] = $parent; $id = self::insertShare($shareQuery); } $postHookData = array( 'itemType' => $itemType, 'itemSource' => $itemSource, 'parent' => $parent, 'shareType' => $shareType, 'uidOwner' => $uidOwner, 'permissions' => $permissions, 'fileSource' => $fileSource, 'id' => $parent, 'token' => $token, 'expirationDate' => $expirationDate, ); $postHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith; $postHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget; $postHookData['fileTarget'] = ($isGroupShare) ? $groupFileTarget : $fileTarget; \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData); return $id ? $id : false; } /** * @param string $itemType * @param string $itemSource * @param int $shareType * @param string $shareWith * @param string $uidOwner * @param int $permissions * @param string|null $itemSourceName * @param null|\DateTime $expirationDate */ private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) { $backend = self::getBackend($itemType); $l = \OC::$server->getL10N('lib'); $result = array(); $column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source'; $checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true); if ($checkReshare) { // Check if attempting to share back to owner if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) { $message = 'Sharing %s failed, because the user %s is the original sharer'; $message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG); throw new \Exception($message_t); } } if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) { // Check if share permissions is granted if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) { if (~(int)$checkReshare['permissions'] & $permissions) { $message = 'Sharing %s failed, because the permissions exceed permissions granted to %s'; $message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OCP\Util::DEBUG); throw new \Exception($message_t); } else { // TODO Don't check if inside folder $result['parent'] = $checkReshare['id']; $result['expirationDate'] = $expirationDate; // $checkReshare['expiration'] could be null and then is always less than any value if(isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) { $result['expirationDate'] = $checkReshare['expiration']; } // only suggest the same name as new target if it is a reshare of the // same file/folder and not the reshare of a child if ($checkReshare[$column] === $itemSource) { $result['filePath'] = $checkReshare['file_target']; $result['itemSource'] = $checkReshare['item_source']; $result['fileSource'] = $checkReshare['file_source']; $result['suggestedItemTarget'] = $checkReshare['item_target']; $result['suggestedFileTarget'] = $checkReshare['file_target']; } else { $result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null; $result['suggestedItemTarget'] = null; $result['suggestedFileTarget'] = null; $result['itemSource'] = $itemSource; $result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null; } } } else { $message = 'Sharing %s failed, because resharing is not allowed'; $message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG); throw new \Exception($message_t); } } else { $result['parent'] = null; $result['suggestedItemTarget'] = null; $result['suggestedFileTarget'] = null; $result['itemSource'] = $itemSource; $result['expirationDate'] = $expirationDate; if (!$backend->isValidSource($itemSource, $uidOwner)) { $message = 'Sharing %s failed, because the sharing backend for ' .'%s could not find its source'; $message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, $itemType), \OCP\Util::DEBUG); throw new \Exception($message_t); } if ($backend instanceof \OCP\Share_Backend_File_Dependent) { $result['filePath'] = $backend->getFilePath($itemSource, $uidOwner); if ($itemType == 'file' || $itemType == 'folder') { $result['fileSource'] = $itemSource; } else { $meta = \OC\Files\Filesystem::getFileInfo($result['filePath']); $result['fileSource'] = $meta['fileid']; } if ($result['fileSource'] == -1) { $message = 'Sharing %s failed, because the file could not be found in the file cache'; $message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource)); \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG); throw new \Exception($message_t); } } else { $result['filePath'] = null; $result['fileSource'] = null; } } return $result; } /** * * @param array $shareData * @return mixed false in case of a failure or the id of the new share */ private static function insertShare(array $shareData) { $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (' .' `item_type`, `item_source`, `item_target`, `share_type`,' .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,' .' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)'); $query->bindValue(1, $shareData['itemType']); $query->bindValue(2, $shareData['itemSource']); $query->bindValue(3, $shareData['itemTarget']); $query->bindValue(4, $shareData['shareType']); $query->bindValue(5, $shareData['shareWith']); $query->bindValue(6, $shareData['uidOwner']); $query->bindValue(7, $shareData['permissions']); $query->bindValue(8, $shareData['shareTime']); $query->bindValue(9, $shareData['fileSource']); $query->bindValue(10, $shareData['fileTarget']); $query->bindValue(11, $shareData['token']); $query->bindValue(12, $shareData['parent']); $query->bindValue(13, $shareData['expiration'], 'datetime'); $result = $query->execute(); $id = false; if ($result) { $id = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share'); } return $id; } /** * Delete all shares with type SHARE_TYPE_LINK */ public static function removeAllLinkShares() { // Delete any link shares $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?'); $result = $query->execute(array(self::SHARE_TYPE_LINK)); while ($item = $result->fetchRow()) { Helper::delete($item['id']); } } /** * In case a password protected link is not yet authenticated this function will return false * * @param array $linkItem * @return boolean */ public static function checkPasswordProtectedShare(array $linkItem) { if (!isset($linkItem['share_with'])) { return true; } if (!isset($linkItem['share_type'])) { return true; } if (!isset($linkItem['id'])) { return true; } if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) { return true; } if ( \OC::$server->getSession()->exists('public_link_authenticated') && \OC::$server->getSession()->get('public_link_authenticated') === (string)$linkItem['id'] ) { return true; } return false; } /** * construct select statement * @param int $format * @param boolean $fileDependent ist it a file/folder share or a generla share * @param string $uidOwner * @return string select statement */ private static function createSelectStatement($format, $fileDependent, $uidOwner = null) { $select = '*'; if ($format == self::FORMAT_STATUSES) { if ($fileDependent) { $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, ' . '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, ' . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, ' . '`uid_initiator`'; } else { $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`'; } } else { if (isset($uidOwner)) { if ($fileDependent) { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,' . ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,' . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, ' . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`'; } else { $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,' . ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`'; } } else { if ($fileDependent) { if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, ' . '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, ' . '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, ' . '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`'; } else { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,' . '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,' . '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,' . '`stime`, `expiration`, `token`, `storage`, `mail_send`,' . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`'; } } } } return $select; } /** * transform db results * @param array $row result */ private static function transformDBResults(&$row) { if (isset($row['id'])) { $row['id'] = (int) $row['id']; } if (isset($row['share_type'])) { $row['share_type'] = (int) $row['share_type']; } if (isset($row['parent'])) { $row['parent'] = (int) $row['parent']; } if (isset($row['file_parent'])) { $row['file_parent'] = (int) $row['file_parent']; } if (isset($row['file_source'])) { $row['file_source'] = (int) $row['file_source']; } if (isset($row['permissions'])) { $row['permissions'] = (int) $row['permissions']; } if (isset($row['storage'])) { $row['storage'] = (int) $row['storage']; } if (isset($row['stime'])) { $row['stime'] = (int) $row['stime']; } if (isset($row['expiration']) && $row['share_type'] !== self::SHARE_TYPE_LINK) { // discard expiration date for non-link shares, which might have been // set by ancient bugs $row['expiration'] = null; } } /** * format result * @param array $items result * @param string $column is it a file share or a general share ('file_target' or 'item_target') * @param \OCP\Share_Backend $backend sharing backend * @param int $format * @param array $parameters additional format parameters * @return array format result */ private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) { if ($format === self::FORMAT_NONE) { return $items; } else if ($format === self::FORMAT_STATUSES) { $statuses = array(); foreach ($items as $item) { if ($item['share_type'] === self::SHARE_TYPE_LINK) { if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) { continue; } $statuses[$item[$column]]['link'] = true; } else if (!isset($statuses[$item[$column]])) { $statuses[$item[$column]]['link'] = false; } if (!empty($item['file_target'])) { $statuses[$item[$column]]['path'] = $item['path']; } } return $statuses; } else { return $backend->formatItems($items, $format, $parameters); } } /** * remove protocol from URL * * @param string $url * @return string */ public static function removeProtocolFromUrl($url) { if (strpos($url, 'https://') === 0) { return substr($url, strlen('https://')); } else if (strpos($url, 'http://') === 0) { return substr($url, strlen('http://')); } return $url; } /** * try http post first with https and then with http as a fallback * * @param string $remoteDomain * @param string $urlSuffix * @param array $fields post parameters * @return array */ private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) { $protocol = 'https://'; $result = [ 'success' => false, 'result' => '', ]; $try = 0; $discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class); while ($result['success'] === false && $try < 2) { $federationEndpoints = $discoveryService->discover($protocol . $remoteDomain, 'FEDERATED_SHARING'); $endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares'; $result = \OC::$server->getHTTPHelper()->post($protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, $fields); $try++; $protocol = 'http://'; } return $result; } /** * send server-to-server share to remote server * * @param string $token * @param string $shareWith * @param string $name * @param int $remote_id * @param string $owner * @return bool */ private static function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) { list($user, $remote) = Helper::splitUserRemote($shareWith); if ($user && $remote) { $url = $remote; $local = \OC::$server->getURLGenerator()->getAbsoluteURL('/'); $fields = array( 'shareWith' => $user, 'token' => $token, 'name' => $name, 'remoteId' => $remote_id, 'owner' => $owner, 'remote' => $local, ); $url = self::removeProtocolFromUrl($url); $result = self::tryHttpPostToShareEndpoint($url, '', $fields); $status = json_decode($result['result'], true); if ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)) { \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]); return true; } } return false; } /** * send server-to-server unshare to remote server * * @param string $remote url * @param int $id share id * @param string $token * @return bool */ private static function sendRemoteUnshare($remote, $id, $token) { $url = rtrim($remote, '/'); $fields = array('token' => $token, 'format' => 'json'); $url = self::removeProtocolFromUrl($url); $result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields); $status = json_decode($result['result'], true); return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)); } /** * check if user can only share with group members * @return bool */ public static function shareWithGroupMembersOnly() { $value = \OC::$server->getAppConfig()->getValue('core', 'shareapi_only_share_with_group_members', 'no'); return ($value === 'yes') ? true : false; } /** * @return bool */ public static function isDefaultExpireDateEnabled() { $defaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no'); return ($defaultExpireDateEnabled === "yes") ? true : false; } /** * @return bool */ public static function enforceDefaultExpireDate() { $enforceDefaultExpireDate = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no'); return ($enforceDefaultExpireDate === "yes") ? true : false; } /** * @return int */ public static function getExpireInterval() { return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7'); } /** * Checks whether the given path is reachable for the given owner * * @param string $path path relative to files * @param string $ownerStorageId storage id of the owner * * @return boolean true if file is reachable, false otherwise */ private static function isFileReachable($path, $ownerStorageId) { // if outside the home storage, file is always considered reachable if (!(substr($ownerStorageId, 0, 6) === 'home::' || substr($ownerStorageId, 0, 13) === 'object::user:' )) { return true; } // if inside the home storage, the file has to be under "/files/" $path = ltrim($path, '/'); if (substr($path, 0, 6) === 'files/') { return true; } return false; } /** * @param IConfig $config * @return bool */ public static function enforcePassword(IConfig $config) { $enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no'); return ($enforcePassword === "yes") ? true : false; } /** * Get all share entries, including non-unique group items * * @param string $owner * @return array */ public static function getAllSharesForOwner($owner) { $query = 'SELECT * FROM `*PREFIX*share` WHERE `uid_owner` = ?'; $result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$owner]); return $result->fetchAll(); } /** * Get all share entries, including non-unique group items for a file * * @param int $id * @return array */ public static function getAllSharesForFileId($id) { $query = 'SELECT * FROM `*PREFIX*share` WHERE `file_source` = ?'; $result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]); return $result->fetchAll(); } /** * @param string $password * @throws \Exception */ private static function verifyPassword($password) { $accepted = true; $message = ''; \OCP\Util::emitHook('\OC\Share', 'verifyPassword', [ 'password' => $password, 'accepted' => &$accepted, 'message' => &$message ]); if (!$accepted) { throw new \Exception($message); } } } private/Share/SearchResultSorter.php 0000604 00000004573 15247130453 0013605 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share; use OCP\ILogger; class SearchResultSorter { private $search; private $encoding; private $key; private $log; /** * @param string $search the search term as was given by the user * @param string $key the array key containing the value that should be compared * against * @param string $encoding optional, encoding to use, defaults to UTF-8 * @param ILogger $log optional */ public function __construct($search, $key, ILogger $log = null, $encoding = 'UTF-8') { $this->encoding = $encoding; $this->key = $key; $this->log = $log; $this->search = mb_strtolower($search, $this->encoding); } /** * User and Group names matching the search term at the beginning shall appear * on top of the share dialog. Following entries in alphabetical order. * Callback function for usort. http://php.net/usort */ public function sort($a, $b) { if(!isset($a[$this->key]) || !isset($b[$this->key])) { if(!is_null($this->log)) { $this->log->error('Sharing dialogue: cannot sort due to ' . 'missing array key', array('app' => 'core')); } return 0; } $nameA = mb_strtolower($a[$this->key], $this->encoding); $nameB = mb_strtolower($b[$this->key], $this->encoding); $i = mb_strpos($nameA, $this->search, 0, $this->encoding); $j = mb_strpos($nameB, $this->search, 0, $this->encoding); if($i === $j || $i > 0 && $j > 0) { return strcmp(mb_strtolower($nameA, $this->encoding), mb_strtolower($nameB, $this->encoding)); } elseif ($i === 0) { return -1; } else { return 1; } } } private/Share/Helper.php 0000604 00000023624 15247130453 0011217 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Miguel Prokop <miguel.prokop@vtu.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Share; use OC\HintException; class Helper extends \OC\Share\Constants { /** * Generate a unique target for the item * @param string $itemType * @param string $itemSource * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string $shareWith User or group the item is being shared with * @param string $uidOwner User that is the owner of shared item * @param string $suggestedTarget The suggested target originating from a reshare (optional) * @param int $groupParent The id of the parent group share (optional) * @throws \Exception * @return string Item target */ public static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $suggestedTarget = null, $groupParent = null) { // FIXME: $uidOwner and $groupParent seems to be unused $backend = \OC\Share\Share::getBackend($itemType); if ($shareType === self::SHARE_TYPE_LINK || $shareType === self::SHARE_TYPE_REMOTE) { if (isset($suggestedTarget)) { return $suggestedTarget; } return $backend->generateTarget($itemSource, false); } else { if ($shareType == self::SHARE_TYPE_USER) { // Share with is a user, so set share type to user and groups $shareType = self::$shareTypeUserAndGroups; } // Check if suggested target exists first if (!isset($suggestedTarget)) { $suggestedTarget = $itemSource; } if ($shareType == self::SHARE_TYPE_GROUP) { $target = $backend->generateTarget($suggestedTarget, false); } else { $target = $backend->generateTarget($suggestedTarget, $shareWith); } return $target; } } /** * Delete all reshares and group share children of an item * @param int $parent Id of item to delete * @param bool $excludeParent If true, exclude the parent from the delete (optional) * @param string $uidOwner The user that the parent was shared with (optional) * @param int $newParent new parent for the childrens * @param bool $excludeGroupChildren exclude group children elements */ public static function delete($parent, $excludeParent = false, $uidOwner = null, $newParent = null, $excludeGroupChildren = false) { $ids = array($parent); $deletedItems = array(); $changeParent = array(); $parents = array($parent); while (!empty($parents)) { $parents = "'".implode("','", $parents)."'"; // Check the owner on the first search of reshares, useful for // finding and deleting the reshares by a single user of a group share $params = array(); if (count($ids) == 1 && isset($uidOwner)) { // FIXME: don't concat $parents, use Docrine's PARAM_INT_ARRAY approach $queryString = 'SELECT `id`, `share_with`, `item_type`, `share_type`, ' . '`item_target`, `file_target`, `parent` ' . 'FROM `*PREFIX*share` ' . 'WHERE `parent` IN ('.$parents.') AND `uid_owner` = ? '; $params[] = $uidOwner; } else { $queryString = 'SELECT `id`, `share_with`, `item_type`, `share_type`, ' . '`item_target`, `file_target`, `parent`, `uid_owner` ' . 'FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.') '; } if ($excludeGroupChildren) { $queryString .= ' AND `share_type` != ?'; $params[] = self::$shareTypeGroupUserUnique; } $query = \OC_DB::prepare($queryString); $result = $query->execute($params); // Reset parents array, only go through loop again if items are found $parents = array(); while ($item = $result->fetchRow()) { $tmpItem = array( 'id' => $item['id'], 'shareWith' => $item['share_with'], 'itemTarget' => $item['item_target'], 'itemType' => $item['item_type'], 'shareType' => (int)$item['share_type'], ); if (isset($item['file_target'])) { $tmpItem['fileTarget'] = $item['file_target']; } // if we have a new parent for the child we remember the child // to update the parent, if not we add it to the list of items // which should be deleted if ($newParent !== null) { $changeParent[] = $item['id']; } else { $deletedItems[] = $tmpItem; $ids[] = $item['id']; $parents[] = $item['id']; } } } if ($excludeParent) { unset($ids[0]); } if (!empty($changeParent)) { $idList = "'".implode("','", $changeParent)."'"; $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` IN ('.$idList.')'); $query->execute(array($newParent)); } if (!empty($ids)) { $idList = "'".implode("','", $ids)."'"; $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$idList.')'); $query->execute(); } return $deletedItems; } /** * get default expire settings defined by the admin * @return array contains 'defaultExpireDateSet', 'enforceExpireDate', 'expireAfterDays' */ public static function getDefaultExpireSetting() { $config = \OC::$server->getConfig(); $defaultExpireSettings = array('defaultExpireDateSet' => false); // get default expire settings $defaultExpireDate = $config->getAppValue('core', 'shareapi_default_expire_date', 'no'); if ($defaultExpireDate === 'yes') { $enforceExpireDate = $config->getAppValue('core', 'shareapi_enforce_expire_date', 'no'); $defaultExpireSettings['defaultExpireDateSet'] = true; $defaultExpireSettings['expireAfterDays'] = (int)($config->getAppValue('core', 'shareapi_expire_after_n_days', '7')); $defaultExpireSettings['enforceExpireDate'] = $enforceExpireDate === 'yes' ? true : false; } return $defaultExpireSettings; } public static function calcExpireDate() { $expireAfter = \OC\Share\Share::getExpireInterval() * 24 * 60 * 60; $expireAt = time() + $expireAfter; $date = new \DateTime(); $date->setTimestamp($expireAt); $date->setTime(0, 0, 0); //$dateString = $date->format('Y-m-d') . ' 00:00:00'; return $date; } /** * calculate expire date * @param array $defaultExpireSettings contains 'defaultExpireDateSet', 'enforceExpireDate', 'expireAfterDays' * @param int $creationTime timestamp when the share was created * @param int $userExpireDate expire timestamp set by the user * @return mixed integer timestamp or False */ public static function calculateExpireDate($defaultExpireSettings, $creationTime, $userExpireDate = null) { $expires = false; $defaultExpires = null; if (!empty($defaultExpireSettings['defaultExpireDateSet'])) { $defaultExpires = $creationTime + $defaultExpireSettings['expireAfterDays'] * 86400; } if (isset($userExpireDate)) { // if the admin decided to enforce the default expire date then we only take // the user defined expire date of it is before the default expire date if ($defaultExpires && !empty($defaultExpireSettings['enforceExpireDate'])) { $expires = min($userExpireDate, $defaultExpires); } else { $expires = $userExpireDate; } } else if ($defaultExpires && !empty($defaultExpireSettings['enforceExpireDate'])) { $expires = $defaultExpires; } return $expires; } /** * Strips away a potential file names and trailing slashes: * - http://localhost * - http://localhost/ * - http://localhost/index.php * - http://localhost/index.php/s/{shareToken} * * all return: http://localhost * * @param string $remote * @return string */ protected static function fixRemoteURL($remote) { $remote = str_replace('\\', '/', $remote); if ($fileNamePosition = strpos($remote, '/index.php')) { $remote = substr($remote, 0, $fileNamePosition); } $remote = rtrim($remote, '/'); return $remote; } /** * split user and remote from federated cloud id * * @param string $id * @return string[] * @throws HintException */ public static function splitUserRemote($id) { try { $cloudId = \OC::$server->getCloudIdManager()->resolveCloudId($id); return [$cloudId->getUser(), $cloudId->getRemote()]; } catch (\InvalidArgumentException $e) { $l = \OC::$server->getL10N('core'); $hint = $l->t('Invalid Federated Cloud ID'); throw new HintException('Invalid Federated Cloud ID', $hint, 0, $e); } } /** * check if two federated cloud IDs refer to the same user * * @param string $user1 * @param string $server1 * @param string $user2 * @param string $server2 * @return bool true if both users and servers are the same */ public static function isSameUserOnSameServer($user1, $server1, $user2, $server2) { $normalizedServer1 = strtolower(\OC\Share\Share::removeProtocolFromUrl($server1)); $normalizedServer2 = strtolower(\OC\Share\Share::removeProtocolFromUrl($server2)); if (rtrim($normalizedServer1, '/') === rtrim($normalizedServer2, '/')) { // FIXME this should be a method in the user management instead \OCP\Util::emitHook( '\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', array('uid' => &$user1) ); \OCP\Util::emitHook( '\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', array('uid' => &$user2) ); if ($user1 === $user2) { return true; } } return false; } } private/NaturalSort_DefaultCollator.php 0000604 00000002102 15247130453 0014344 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author AW-UC <git@a-wesemann.de> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; class NaturalSort_DefaultCollator { public function compare($a, $b) { $result = strcasecmp($a, $b); if ($result === 0) { if ($a === $b) { return 0; } return ($a > $b) ? -1 : 1; } return ($result < 0) ? -1 : 1; } } private/Tags.php 0000604 00000053033 15247130453 0007631 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bernhard Reiter <ockham@raz.or.at> * @author derkostka <sebastian.kostka@gmail.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ /** * Class for easily tagging objects by their id * * A tag can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or * anything else that is either parsed from a vobject or that the user chooses * to add. * Tag names are not case-sensitive, but will be saved with the case they * are entered in. If a user already has a tag 'family' for a type, and * tries to add a tag named 'Family' it will be silently ignored. */ namespace OC; use OC\Tagging\Tag; use OC\Tagging\TagMapper; use OCP\DB\QueryBuilder\IQueryBuilder; class Tags implements \OCP\ITags { /** * Tags * * @var array */ private $tags = array(); /** * Used for storing objectid/categoryname pairs while rescanning. * * @var array */ private static $relations = array(); /** * Type * * @var string */ private $type; /** * User * * @var string */ private $user; /** * Are we including tags for shared items? * * @var bool */ private $includeShared = false; /** * The current user, plus any owners of the items shared with the current * user, if $this->includeShared === true. * * @var array */ private $owners = array(); /** * The Mapper we're using to communicate our Tag objects to the database. * * @var TagMapper */ private $mapper; /** * The sharing backend for objects of $this->type. Required if * $this->includeShared === true to determine ownership of items. * * @var \OCP\Share_Backend */ private $backend; const TAG_TABLE = '*PREFIX*vcategory'; const RELATION_TABLE = '*PREFIX*vcategory_to_object'; const TAG_FAVORITE = '_$!<Favorite>!$_'; /** * Constructor. * * @param TagMapper $mapper Instance of the TagMapper abstraction layer. * @param string $user The user whose data the object will operate on. * @param string $type The type of items for which tags will be loaded. * @param array $defaultTags Tags that should be created at construction. * @param boolean $includeShared Whether to include tags for items shared with this user by others. */ public function __construct(TagMapper $mapper, $user, $type, $defaultTags = array(), $includeShared = false) { $this->mapper = $mapper; $this->user = $user; $this->type = $type; $this->includeShared = $includeShared; $this->owners = array($this->user); if ($this->includeShared) { $this->owners = array_merge($this->owners, \OC\Share\Share::getSharedItemsOwners($this->user, $this->type, true)); $this->backend = \OC\Share\Share::getBackend($this->type); } $this->tags = $this->mapper->loadTags($this->owners, $this->type); if(count($defaultTags) > 0 && count($this->tags) === 0) { $this->addMultiple($defaultTags, true); } } /** * Check if any tags are saved for this type and user. * * @return boolean. */ public function isEmpty() { return count($this->tags) === 0; } /** * Returns an array mapping a given tag's properties to its values: * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype'] * * @param string $id The ID of the tag that is going to be mapped * @return array|false */ public function getTag($id) { $key = $this->getTagById($id); if ($key !== false) { return $this->tagMap($this->tags[$key]); } return false; } /** * Get the tags for a specific user. * * This returns an array with maps containing each tag's properties: * [ * ['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'], * ['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'], * ] * * @return array */ public function getTags() { if(!count($this->tags)) { return array(); } usort($this->tags, function($a, $b) { return strnatcasecmp($a->getName(), $b->getName()); }); $tagMap = array(); foreach($this->tags as $tag) { if($tag->getName() !== self::TAG_FAVORITE) { $tagMap[] = $this->tagMap($tag); } } return $tagMap; } /** * Return only the tags owned by the given user, omitting any tags shared * by other users. * * @param string $user The user whose tags are to be checked. * @return array An array of Tag objects. */ public function getTagsForUser($user) { return array_filter($this->tags, function($tag) use($user) { return $tag->getOwner() === $user; } ); } /** * Get the list of tags for the given ids. * * @param array $objIds array of object ids * @return array|boolean of tags id as key to array of tag names * or false if an error occurred */ public function getTagsForObjects(array $objIds) { $entries = array(); try { $conn = \OC::$server->getDatabaseConnection(); $chunks = array_chunk($objIds, 900, false); foreach ($chunks as $chunk) { $result = $conn->executeQuery( 'SELECT `category`, `categoryid`, `objid` ' . 'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' . 'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)', array($this->user, $this->type, $chunk), array(null, null, IQueryBuilder::PARAM_INT_ARRAY) ); while ($row = $result->fetch()) { $objId = (int)$row['objid']; if (!isset($entries[$objId])) { $entries[$objId] = array(); } $entries[$objId][] = $row['category']; } if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage(), \OCP\Util::ERROR); return false; } } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); return false; } return $entries; } /** * Get the a list if items tagged with $tag. * * Throws an exception if the tag could not be found. * * @param string $tag Tag id or name. * @return array|false An array of object ids or false on error. * @throws \Exception */ public function getIdsForTag($tag) { $result = null; $tagId = false; if(is_numeric($tag)) { $tagId = $tag; } elseif(is_string($tag)) { $tag = trim($tag); if($tag === '') { \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', \OCP\Util::DEBUG); return false; } $tagId = $this->getTagId($tag); } if($tagId === false) { $l10n = \OC::$server->getL10N('core'); throw new \Exception( $l10n->t('Could not find category "%s"', $tag) ); } $ids = array(); $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE . '` WHERE `categoryid` = ?'; try { $stmt = \OCP\DB::prepare($sql); $result = $stmt->execute(array($tagId)); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage(), \OCP\Util::ERROR); return false; } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); return false; } if(!is_null($result)) { while( $row = $result->fetchRow()) { $id = (int)$row['objid']; if ($this->includeShared) { // We have to check if we are really allowed to access the // items that are tagged with $tag. To that end, we ask the // corresponding sharing backend if the item identified by $id // is owned by any of $this->owners. foreach ($this->owners as $owner) { if ($this->backend->isValidSource($id, $owner)) { $ids[] = $id; break; } } } else { $ids[] = $id; } } } return $ids; } /** * Checks whether a tag is saved for the given user, * disregarding the ones shared with him or her. * * @param string $name The tag name to check for. * @param string $user The user whose tags are to be checked. * @return bool */ public function userHasTag($name, $user) { $key = $this->array_searchi($name, $this->getTagsForUser($user)); return ($key !== false) ? $this->tags[$key]->getId() : false; } /** * Checks whether a tag is saved for or shared with the current user. * * @param string $name The tag name to check for. * @return bool */ public function hasTag($name) { return $this->getTagId($name) !== false; } /** * Add a new tag. * * @param string $name A string with a name of the tag * @return false|int the id of the added tag or false on error. */ public function add($name) { $name = trim($name); if($name === '') { \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', \OCP\Util::DEBUG); return false; } if($this->userHasTag($name, $this->user)) { \OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', \OCP\Util::DEBUG); return false; } try { $tag = new Tag($this->user, $this->type, $name); $tag = $this->mapper->insert($tag); $this->tags[] = $tag; } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); return false; } \OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), \OCP\Util::DEBUG); return $tag->getId(); } /** * Rename tag. * * @param string|integer $from The name or ID of the existing tag * @param string $to The new name of the tag. * @return bool */ public function rename($from, $to) { $from = trim($from); $to = trim($to); if($to === '' || $from === '') { \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', \OCP\Util::DEBUG); return false; } if (is_numeric($from)) { $key = $this->getTagById($from); } else { $key = $this->getTagByName($from); } if($key === false) { \OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', \OCP\Util::DEBUG); return false; } $tag = $this->tags[$key]; if($this->userHasTag($to, $tag->getOwner())) { \OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', \OCP\Util::DEBUG); return false; } try { $tag->setName($to); $this->tags[$key] = $this->mapper->update($tag); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); return false; } return true; } /** * Add a list of new tags. * * @param string[] $names A string with a name or an array of strings containing * the name(s) of the tag(s) to add. * @param bool $sync When true, save the tags * @param int|null $id int Optional object id to add to this|these tag(s) * @return bool Returns false on error. */ public function addMultiple($names, $sync=false, $id = null) { if(!is_array($names)) { $names = array($names); } $names = array_map('trim', $names); array_filter($names); $newones = array(); foreach($names as $name) { if(!$this->hasTag($name) && $name !== '') { $newones[] = new Tag($this->user, $this->type, $name); } if(!is_null($id) ) { // Insert $objectid, $categoryid pairs if not exist. self::$relations[] = array('objid' => $id, 'tag' => $name); } } $this->tags = array_merge($this->tags, $newones); if($sync === true) { $this->save(); } return true; } /** * Save the list of tags and their object relations */ protected function save() { if(is_array($this->tags)) { foreach($this->tags as $tag) { try { if (!$this->mapper->tagExists($tag)) { $this->mapper->insert($tag); } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); } } // reload tags to get the proper ids. $this->tags = $this->mapper->loadTags($this->owners, $this->type); \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true), \OCP\Util::DEBUG); // Loop through temporarily cached objectid/tagname pairs // and save relations. $tags = $this->tags; // For some reason this is needed or array_search(i) will return 0..? ksort($tags); foreach(self::$relations as $relation) { $tagId = $this->getTagId($relation['tag']); \OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, \OCP\Util::DEBUG); if($tagId) { try { \OCP\DB::insertIfNotExist(self::RELATION_TABLE, array( 'objid' => $relation['objid'], 'categoryid' => $tagId, 'type' => $this->type, )); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); } } } self::$relations = array(); // reset } else { \OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! ' . print_r($this->tags, true), \OCP\Util::ERROR); } } /** * Delete tags and tag/object relations for a user. * * For hooking up on post_deleteUser * * @param array $arguments */ public static function post_deleteUser($arguments) { // Find all objectid/tagId pairs. $result = null; try { $stmt = \OCP\DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` ' . 'WHERE `uid` = ?'); $result = $stmt->execute(array($arguments['uid'])); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage(), \OCP\Util::ERROR); } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); } if(!is_null($result)) { try { $stmt = \OCP\DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` ' . 'WHERE `categoryid` = ?'); while( $row = $result->fetchRow()) { try { $stmt->execute(array($row['id'])); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); } } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); } } try { $stmt = \OCP\DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` ' . 'WHERE `uid` = ?'); $result = $stmt->execute(array($arguments['uid'])); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage(), \OCP\Util::ERROR); } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__ . ', exception: ' . $e->getMessage(), \OCP\Util::ERROR); } } /** * Delete tag/object relations from the db * * @param array $ids The ids of the objects * @return boolean Returns false on error. */ public function purgeObjects(array $ids) { if(count($ids) === 0) { // job done ;) return true; } $updates = $ids; try { $query = 'DELETE FROM `' . self::RELATION_TABLE . '` '; $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) '; $query .= 'AND `type`= ?'; $updates[] = $this->type; $stmt = \OCP\DB::prepare($query); $result = $stmt->execute($updates); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage(), \OCP\Util::ERROR); return false; } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: ' . $e->getMessage(), \OCP\Util::ERROR); return false; } return true; } /** * Get favorites for an object type * * @return array|false An array of object ids. */ public function getFavorites() { try { return $this->getIdsForTag(self::TAG_FAVORITE); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: ' . $e->getMessage(), \OCP\Util::DEBUG); return array(); } } /** * Add an object to favorites * * @param int $objid The id of the object * @return boolean */ public function addToFavorites($objid) { if(!$this->userHasTag(self::TAG_FAVORITE, $this->user)) { $this->add(self::TAG_FAVORITE); } return $this->tagAs($objid, self::TAG_FAVORITE); } /** * Remove an object from favorites * * @param int $objid The id of the object * @return boolean */ public function removeFromFavorites($objid) { return $this->unTag($objid, self::TAG_FAVORITE); } /** * Creates a tag/object relation. * * @param int $objid The id of the object * @param string $tag The id or name of the tag * @return boolean Returns false on error. */ public function tagAs($objid, $tag) { if(is_string($tag) && !is_numeric($tag)) { $tag = trim($tag); if($tag === '') { \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', \OCP\Util::DEBUG); return false; } if(!$this->hasTag($tag)) { $this->add($tag); } $tagId = $this->getTagId($tag); } else { $tagId = $tag; } try { \OCP\DB::insertIfNotExist(self::RELATION_TABLE, array( 'objid' => $objid, 'categoryid' => $tagId, 'type' => $this->type, )); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); return false; } return true; } /** * Delete single tag/object relation from the db * * @param int $objid The id of the object * @param string $tag The id or name of the tag * @return boolean */ public function unTag($objid, $tag) { if(is_string($tag) && !is_numeric($tag)) { $tag = trim($tag); if($tag === '') { \OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', \OCP\Util::DEBUG); return false; } $tagId = $this->getTagId($tag); } else { $tagId = $tag; } try { $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?'; $stmt = \OCP\DB::prepare($sql); $stmt->execute(array($objid, $tagId, $this->type)); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); return false; } return true; } /** * Delete tags from the database. * * @param string[]|integer[] $names An array of tags (names or IDs) to delete * @return bool Returns false on error */ public function delete($names) { if(!is_array($names)) { $names = array($names); } $names = array_map('trim', $names); array_filter($names); \OCP\Util::writeLog('core', __METHOD__ . ', before: ' . print_r($this->tags, true), \OCP\Util::DEBUG); foreach($names as $name) { $id = null; if (is_numeric($name)) { $key = $this->getTagById($name); } else { $key = $this->getTagByName($name); } if ($key !== false) { $tag = $this->tags[$key]; $id = $tag->getId(); unset($this->tags[$key]); $this->mapper->delete($tag); } else { \OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name . ': not found.', \OCP\Util::ERROR); } if(!is_null($id) && $id !== false) { try { $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' . 'WHERE `categoryid` = ?'; $stmt = \OCP\DB::prepare($sql); $result = $stmt->execute(array($id)); if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage(), \OCP\Util::ERROR); return false; } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); return false; } } } return true; } // case-insensitive array_search protected function array_searchi($needle, $haystack, $mem='getName') { if(!is_array($haystack)) { return false; } return array_search(strtolower($needle), array_map( function($tag) use($mem) { return strtolower(call_user_func(array($tag, $mem))); }, $haystack) ); } /** * Get a tag's ID. * * @param string $name The tag name to look for. * @return string|bool The tag's id or false if no matching tag is found. */ private function getTagId($name) { $key = $this->array_searchi($name, $this->tags); if ($key !== false) { return $this->tags[$key]->getId(); } return false; } /** * Get a tag by its name. * * @param string $name The tag name. * @return integer|bool The tag object's offset within the $this->tags * array or false if it doesn't exist. */ private function getTagByName($name) { return $this->array_searchi($name, $this->tags, 'getName'); } /** * Get a tag by its ID. * * @param string $id The tag ID to look for. * @return integer|bool The tag object's offset within the $this->tags * array or false if it doesn't exist. */ private function getTagById($id) { return $this->array_searchi($id, $this->tags, 'getId'); } /** * Returns an array mapping a given tag's properties to its values: * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype'] * * @param Tag $tag The tag that is going to be mapped * @return array */ private function tagMap(Tag $tag) { return array( 'id' => $tag->getId(), 'name' => $tag->getName(), 'owner' => $tag->getOwner(), 'type' => $tag->getType() ); } } private/CapabilitiesManager.php 0000604 00000004215 15247130453 0012615 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OCP\AppFramework\QueryException; use OCP\Capabilities\ICapability; use OCP\ILogger; class CapabilitiesManager { /** @var \Closure[] */ private $capabilities = array(); /** @var ILogger */ private $logger; public function __construct(ILogger $logger) { $this->logger = $logger; } /** * Get an array of al the capabilities that are registered at this manager * * @throws \InvalidArgumentException * @return array */ public function getCapabilities() { $capabilities = []; foreach($this->capabilities as $capability) { try { $c = $capability(); } catch (QueryException $e) { $this->logger->error('CapabilitiesManager: {message}', ['app' => 'core', 'message' => $e->getMessage()]); continue; } if ($c instanceof ICapability) { $capabilities = array_replace_recursive($capabilities, $c->getCapabilities()); } else { throw new \InvalidArgumentException('The given Capability (' . get_class($c) . ') does not implement the ICapability interface'); } } return $capabilities; } /** * In order to improve lazy loading a closure can be registered which will be called in case * capabilities are actually requested * * $callable has to return an instance of OCP\Capabilities\ICapability * * @param \Closure $callable */ public function registerCapability(\Closure $callable) { array_push($this->capabilities, $callable); } } private/Server.php 0000604 00000147343 15247130453 0010211 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> * @author Bernhard Reiter <ockham@raz.or.at> * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Björn Schießle <bjoern@schiessle.org> * @author Christopher Schäpers <kondou@ts.unde.re> * @author Christoph Wurst <christoph@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Sander <brantje@gmail.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * @author Roger Szabo <roger.szabo@web.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; use OC\App\AppManager; use OC\App\AppStore\Bundles\BundleFetcher; use OC\App\AppStore\Fetcher\AppFetcher; use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\AppFramework\Http\Request; use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; use OC\Authentication\LoginCredentials\Store; use OC\Command\AsyncBus; use OC\Contacts\ContactsMenu\ActionFactory; use OC\Diagnostics\EventLogger; use OC\Diagnostics\NullEventLogger; use OC\Diagnostics\NullQueryLogger; use OC\Diagnostics\QueryLogger; use OC\Federation\CloudIdManager; use OC\Files\Config\UserMountCache; use OC\Files\Config\UserMountCacheListener; use OC\Files\Mount\CacheMountProvider; use OC\Files\Mount\LocalHomeMountProvider; use OC\Files\Mount\ObjectHomeMountProvider; use OC\Files\Node\HookConnector; use OC\Files\Node\LazyRoot; use OC\Files\Node\Root; use OC\Files\View; use OC\Http\Client\ClientService; use OC\IntegrityCheck\Checker; use OC\IntegrityCheck\Helpers\AppLocator; use OC\IntegrityCheck\Helpers\EnvironmentHelper; use OC\IntegrityCheck\Helpers\FileAccessHelper; use OC\Lock\DBLockingProvider; use OC\Lock\MemcacheLockingProvider; use OC\Lock\NoopLockingProvider; use OC\Lockdown\LockdownManager; use OC\Mail\Mailer; use OC\Memcache\ArrayCache; use OC\Memcache\Factory; use OC\Notification\Manager; use OC\OCS\DiscoveryService; use OC\Repair\NC11\CleanPreviewsBackgroundJob; use OC\RichObjectStrings\Validator; use OC\Security\Bruteforce\Throttler; use OC\Security\CertificateManager; use OC\Security\CSP\ContentSecurityPolicyManager; use OC\Security\Crypto; use OC\Security\CSP\ContentSecurityPolicyNonceManager; use OC\Security\CSRF\CsrfTokenGenerator; use OC\Security\CSRF\CsrfTokenManager; use OC\Security\CSRF\TokenStorage\SessionStorage; use OC\Security\Hasher; use OC\Security\CredentialsManager; use OC\Security\SecureRandom; use OC\Security\TrustedDomainHelper; use OC\Session\CryptoWrapper; use OC\Share20\ShareHelper; use OC\Tagging\TagMapper; use OC\Template\SCSSCacher; use OCA\Theming\ThemingDefaults; use OCP\App\IAppManager; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Defaults; use OCA\Theming\Util; use OCP\Federation\ICloudIdManager; use OCP\Authentication\LoginCredentials\IStore; use OCP\ICacheFactory; use OCP\IDBConnection; use OCP\IL10N; use OCP\IServerContainer; use OCP\ITempManager; use OCP\Contacts\ContactsMenu\IActionFactory; use OCP\IURLGenerator; use OCP\RichObjectStrings\IValidator; use OCP\Security\IContentSecurityPolicyManager; use OCP\Share\IShareHelper; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class Server * * @package OC * * TODO: hookup all manager classes */ class Server extends ServerContainer implements IServerContainer { /** @var string */ private $webRoot; /** * @param string $webRoot * @param \OC\Config $config */ public function __construct($webRoot, \OC\Config $config) { parent::__construct(); $this->webRoot = $webRoot; $this->registerService(\OCP\IServerContainer::class, function(IServerContainer $c) { return $c; }); $this->registerAlias(\OCP\Contacts\IManager::class, \OC\ContactsManager::class); $this->registerAlias('ContactsManager', \OCP\Contacts\IManager::class); $this->registerAlias(IActionFactory::class, ActionFactory::class); $this->registerService(\OCP\IPreview::class, function (Server $c) { return new PreviewManager( $c->getConfig(), $c->getRootFolder(), $c->getAppDataDir('preview'), $c->getEventDispatcher(), $c->getSession()->get('user_id') ); }); $this->registerAlias('PreviewManager', \OCP\IPreview::class); $this->registerService(\OC\Preview\Watcher::class, function (Server $c) { return new \OC\Preview\Watcher( $c->getAppDataDir('preview') ); }); $this->registerService('EncryptionManager', function (Server $c) { $view = new View(); $util = new Encryption\Util( $view, $c->getUserManager(), $c->getGroupManager(), $c->getConfig() ); return new Encryption\Manager( $c->getConfig(), $c->getLogger(), $c->getL10N('core'), new View(), $util, new ArrayCache() ); }); $this->registerService('EncryptionFileHelper', function (Server $c) { $util = new Encryption\Util( new View(), $c->getUserManager(), $c->getGroupManager(), $c->getConfig() ); return new Encryption\File( $util, $c->getRootFolder(), $c->getShareManager() ); }); $this->registerService('EncryptionKeyStorage', function (Server $c) { $view = new View(); $util = new Encryption\Util( $view, $c->getUserManager(), $c->getGroupManager(), $c->getConfig() ); return new Encryption\Keys\Storage($view, $util); }); $this->registerService('TagMapper', function (Server $c) { return new TagMapper($c->getDatabaseConnection()); }); $this->registerService(\OCP\ITagManager::class, function (Server $c) { $tagMapper = $c->query('TagMapper'); return new TagManager($tagMapper, $c->getUserSession()); }); $this->registerAlias('TagManager', \OCP\ITagManager::class); $this->registerService('SystemTagManagerFactory', function (Server $c) { $config = $c->getConfig(); $factoryClass = $config->getSystemValue('systemtags.managerFactory', '\OC\SystemTag\ManagerFactory'); /** @var \OC\SystemTag\ManagerFactory $factory */ $factory = new $factoryClass($this); return $factory; }); $this->registerService(\OCP\SystemTag\ISystemTagManager::class, function (Server $c) { return $c->query('SystemTagManagerFactory')->getManager(); }); $this->registerAlias('SystemTagManager', \OCP\SystemTag\ISystemTagManager::class); $this->registerService(\OCP\SystemTag\ISystemTagObjectMapper::class, function (Server $c) { return $c->query('SystemTagManagerFactory')->getObjectMapper(); }); $this->registerService('RootFolder', function (Server $c) { $manager = \OC\Files\Filesystem::getMountManager(null); $view = new View(); $root = new Root( $manager, $view, null, $c->getUserMountCache(), $this->getLogger(), $this->getUserManager() ); $connector = new HookConnector($root, $view); $connector->viewToNode(); $previewConnector = new \OC\Preview\WatcherConnector($root, $c->getSystemConfig()); $previewConnector->connectWatcher(); return $root; }); $this->registerAlias('SystemTagObjectMapper', \OCP\SystemTag\ISystemTagObjectMapper::class); $this->registerService(\OCP\Files\IRootFolder::class, function(Server $c) { return new LazyRoot(function() use ($c) { return $c->query('RootFolder'); }); }); $this->registerAlias('LazyRootFolder', \OCP\Files\IRootFolder::class); $this->registerService(\OCP\IUserManager::class, function (Server $c) { $config = $c->getConfig(); return new \OC\User\Manager($config); }); $this->registerAlias('UserManager', \OCP\IUserManager::class); $this->registerService(\OCP\IGroupManager::class, function (Server $c) { $groupManager = new \OC\Group\Manager($this->getUserManager(), $this->getLogger()); $groupManager->listen('\OC\Group', 'preCreate', function ($gid) { \OC_Hook::emit('OC_Group', 'pre_createGroup', array('run' => true, 'gid' => $gid)); }); $groupManager->listen('\OC\Group', 'postCreate', function (\OC\Group\Group $gid) { \OC_Hook::emit('OC_User', 'post_createGroup', array('gid' => $gid->getGID())); }); $groupManager->listen('\OC\Group', 'preDelete', function (\OC\Group\Group $group) { \OC_Hook::emit('OC_Group', 'pre_deleteGroup', array('run' => true, 'gid' => $group->getGID())); }); $groupManager->listen('\OC\Group', 'postDelete', function (\OC\Group\Group $group) { \OC_Hook::emit('OC_User', 'post_deleteGroup', array('gid' => $group->getGID())); }); $groupManager->listen('\OC\Group', 'preAddUser', function (\OC\Group\Group $group, \OC\User\User $user) { \OC_Hook::emit('OC_Group', 'pre_addToGroup', array('run' => true, 'uid' => $user->getUID(), 'gid' => $group->getGID())); }); $groupManager->listen('\OC\Group', 'postAddUser', function (\OC\Group\Group $group, \OC\User\User $user) { \OC_Hook::emit('OC_Group', 'post_addToGroup', array('uid' => $user->getUID(), 'gid' => $group->getGID())); //Minimal fix to keep it backward compatible TODO: clean up all the GroupManager hooks \OC_Hook::emit('OC_User', 'post_addToGroup', array('uid' => $user->getUID(), 'gid' => $group->getGID())); }); return $groupManager; }); $this->registerAlias('GroupManager', \OCP\IGroupManager::class); $this->registerService(Store::class, function(Server $c) { $session = $c->getSession(); if (\OC::$server->getSystemConfig()->getValue('installed', false)) { $tokenProvider = $c->query('OC\Authentication\Token\IProvider'); } else { $tokenProvider = null; } $logger = $c->getLogger(); return new Store($session, $logger, $tokenProvider); }); $this->registerAlias(IStore::class, Store::class); $this->registerService('OC\Authentication\Token\DefaultTokenMapper', function (Server $c) { $dbConnection = $c->getDatabaseConnection(); return new Authentication\Token\DefaultTokenMapper($dbConnection); }); $this->registerService('OC\Authentication\Token\DefaultTokenProvider', function (Server $c) { $mapper = $c->query('OC\Authentication\Token\DefaultTokenMapper'); $crypto = $c->getCrypto(); $config = $c->getConfig(); $logger = $c->getLogger(); $timeFactory = new TimeFactory(); return new \OC\Authentication\Token\DefaultTokenProvider($mapper, $crypto, $config, $logger, $timeFactory); }); $this->registerAlias('OC\Authentication\Token\IProvider', 'OC\Authentication\Token\DefaultTokenProvider'); $this->registerService(\OCP\IUserSession::class, function (Server $c) { $manager = $c->getUserManager(); $session = new \OC\Session\Memory(''); $timeFactory = new TimeFactory(); // Token providers might require a working database. This code // might however be called when ownCloud is not yet setup. if (\OC::$server->getSystemConfig()->getValue('installed', false)) { $defaultTokenProvider = $c->query('OC\Authentication\Token\IProvider'); } else { $defaultTokenProvider = null; } $userSession = new \OC\User\Session($manager, $session, $timeFactory, $defaultTokenProvider, $c->getConfig(), $c->getSecureRandom(), $c->getLockdownManager()); $userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password) { \OC_Hook::emit('OC_User', 'pre_createUser', array('run' => true, 'uid' => $uid, 'password' => $password)); }); $userSession->listen('\OC\User', 'postCreateUser', function ($user, $password) { /** @var $user \OC\User\User */ \OC_Hook::emit('OC_User', 'post_createUser', array('uid' => $user->getUID(), 'password' => $password)); }); $userSession->listen('\OC\User', 'preDelete', function ($user) { /** @var $user \OC\User\User */ \OC_Hook::emit('OC_User', 'pre_deleteUser', array('run' => true, 'uid' => $user->getUID())); }); $userSession->listen('\OC\User', 'postDelete', function ($user) { /** @var $user \OC\User\User */ \OC_Hook::emit('OC_User', 'post_deleteUser', array('uid' => $user->getUID())); }); $userSession->listen('\OC\User', 'preSetPassword', function ($user, $password, $recoveryPassword) { /** @var $user \OC\User\User */ \OC_Hook::emit('OC_User', 'pre_setPassword', array('run' => true, 'uid' => $user->getUID(), 'password' => $password, 'recoveryPassword' => $recoveryPassword)); }); $userSession->listen('\OC\User', 'postSetPassword', function ($user, $password, $recoveryPassword) { /** @var $user \OC\User\User */ \OC_Hook::emit('OC_User', 'post_setPassword', array('run' => true, 'uid' => $user->getUID(), 'password' => $password, 'recoveryPassword' => $recoveryPassword)); }); $userSession->listen('\OC\User', 'preLogin', function ($uid, $password) { \OC_Hook::emit('OC_User', 'pre_login', array('run' => true, 'uid' => $uid, 'password' => $password)); }); $userSession->listen('\OC\User', 'postLogin', function ($user, $password) { /** @var $user \OC\User\User */ \OC_Hook::emit('OC_User', 'post_login', array('run' => true, 'uid' => $user->getUID(), 'password' => $password)); }); $userSession->listen('\OC\User', 'postRememberedLogin', function ($user, $password) { /** @var $user \OC\User\User */ \OC_Hook::emit('OC_User', 'post_login', array('run' => true, 'uid' => $user->getUID(), 'password' => $password)); }); $userSession->listen('\OC\User', 'logout', function () { \OC_Hook::emit('OC_User', 'logout', array()); }); $userSession->listen('\OC\User', 'changeUser', function ($user, $feature, $value, $oldValue) { /** @var $user \OC\User\User */ \OC_Hook::emit('OC_User', 'changeUser', array('run' => true, 'user' => $user, 'feature' => $feature, 'value' => $value, 'old_value' => $oldValue)); }); return $userSession; }); $this->registerAlias('UserSession', \OCP\IUserSession::class); $this->registerService(\OC\Authentication\TwoFactorAuth\Manager::class, function (Server $c) { return new \OC\Authentication\TwoFactorAuth\Manager( $c->getAppManager(), $c->getSession(), $c->getConfig(), $c->getActivityManager(), $c->getLogger(), $c->query(\OC\Authentication\Token\IProvider::class), $c->query(ITimeFactory::class) ); }); $this->registerAlias(\OCP\INavigationManager::class, \OC\NavigationManager::class); $this->registerAlias('NavigationManager', \OCP\INavigationManager::class); $this->registerService(\OC\AllConfig::class, function (Server $c) { return new \OC\AllConfig( $c->getSystemConfig() ); }); $this->registerAlias('AllConfig', \OC\AllConfig::class); $this->registerAlias(\OCP\IConfig::class, \OC\AllConfig::class); $this->registerService('SystemConfig', function ($c) use ($config) { return new \OC\SystemConfig($config); }); $this->registerService(\OC\AppConfig::class, function (Server $c) { return new \OC\AppConfig($c->getDatabaseConnection()); }); $this->registerAlias('AppConfig', \OC\AppConfig::class); $this->registerAlias(\OCP\IAppConfig::class, \OC\AppConfig::class); $this->registerService(\OCP\L10N\IFactory::class, function (Server $c) { return new \OC\L10N\Factory( $c->getConfig(), $c->getRequest(), $c->getUserSession(), \OC::$SERVERROOT ); }); $this->registerAlias('L10NFactory', \OCP\L10N\IFactory::class); $this->registerService(\OCP\IURLGenerator::class, function (Server $c) { $config = $c->getConfig(); $cacheFactory = $c->getMemCacheFactory(); $request = $c->getRequest(); return new \OC\URLGenerator( $config, $cacheFactory, $request ); }); $this->registerAlias('URLGenerator', \OCP\IURLGenerator::class); $this->registerService('AppHelper', function ($c) { return new \OC\AppHelper(); }); $this->registerAlias('AppFetcher', AppFetcher::class); $this->registerAlias('CategoryFetcher', CategoryFetcher::class); $this->registerService(\OCP\ICache::class, function ($c) { return new Cache\File(); }); $this->registerAlias('UserCache', \OCP\ICache::class); $this->registerService(Factory::class, function (Server $c) { $arrayCacheFactory = new \OC\Memcache\Factory('', $c->getLogger(), '\\OC\\Memcache\\ArrayCache', '\\OC\\Memcache\\ArrayCache', '\\OC\\Memcache\\ArrayCache' ); $config = $c->getConfig(); $request = $c->getRequest(); $urlGenerator = new URLGenerator($config, $arrayCacheFactory, $request); if ($config->getSystemValue('installed', false) && !(defined('PHPUNIT_RUN') && PHPUNIT_RUN)) { $v = \OC_App::getAppVersions(); $v['core'] = implode(',', \OC_Util::getVersion()); $version = implode(',', $v); $instanceId = \OC_Util::getInstanceId(); $path = \OC::$SERVERROOT; $prefix = md5($instanceId . '-' . $version . '-' . $path . '-' . $urlGenerator->getBaseUrl()); return new \OC\Memcache\Factory($prefix, $c->getLogger(), $config->getSystemValue('memcache.local', null), $config->getSystemValue('memcache.distributed', null), $config->getSystemValue('memcache.locking', null) ); } return $arrayCacheFactory; }); $this->registerAlias('MemCacheFactory', Factory::class); $this->registerAlias(ICacheFactory::class, Factory::class); $this->registerService('RedisFactory', function (Server $c) { $systemConfig = $c->getSystemConfig(); return new RedisFactory($systemConfig); }); $this->registerService(\OCP\Activity\IManager::class, function (Server $c) { return new \OC\Activity\Manager( $c->getRequest(), $c->getUserSession(), $c->getConfig(), $c->query(IValidator::class) ); }); $this->registerAlias('ActivityManager', \OCP\Activity\IManager::class); $this->registerService(\OCP\Activity\IEventMerger::class, function (Server $c) { return new \OC\Activity\EventMerger( $c->getL10N('lib') ); }); $this->registerAlias(IValidator::class, Validator::class); $this->registerService(\OCP\IAvatarManager::class, function (Server $c) { return new AvatarManager( $c->getUserManager(), $c->getAppDataDir('avatar'), $c->getL10N('lib'), $c->getLogger(), $c->getConfig() ); }); $this->registerAlias('AvatarManager', \OCP\IAvatarManager::class); $this->registerService(\OCP\ILogger::class, function (Server $c) { $logType = $c->query('AllConfig')->getSystemValue('log_type', 'file'); $logger = Log::getLogClass($logType); call_user_func(array($logger, 'init')); return new Log($logger); }); $this->registerAlias('Logger', \OCP\ILogger::class); $this->registerService(\OCP\BackgroundJob\IJobList::class, function (Server $c) { $config = $c->getConfig(); return new \OC\BackgroundJob\JobList( $c->getDatabaseConnection(), $config, new TimeFactory() ); }); $this->registerAlias('JobList', \OCP\BackgroundJob\IJobList::class); $this->registerService(\OCP\Route\IRouter::class, function (Server $c) { $cacheFactory = $c->getMemCacheFactory(); $logger = $c->getLogger(); if ($cacheFactory->isAvailable()) { $router = new \OC\Route\CachingRouter($cacheFactory->create('route'), $logger); } else { $router = new \OC\Route\Router($logger); } return $router; }); $this->registerAlias('Router', \OCP\Route\IRouter::class); $this->registerService(\OCP\ISearch::class, function ($c) { return new Search(); }); $this->registerAlias('Search', \OCP\ISearch::class); $this->registerService(\OC\Security\RateLimiting\Limiter::class, function($c) { return new \OC\Security\RateLimiting\Limiter( $this->getUserSession(), $this->getRequest(), new \OC\AppFramework\Utility\TimeFactory(), $c->query(\OC\Security\RateLimiting\Backend\IBackend::class) ); }); $this->registerService(\OC\Security\RateLimiting\Backend\IBackend::class, function($c) { return new \OC\Security\RateLimiting\Backend\MemoryCache( $this->getMemCacheFactory(), new \OC\AppFramework\Utility\TimeFactory() ); }); $this->registerService(\OCP\Security\ISecureRandom::class, function ($c) { return new SecureRandom(); }); $this->registerAlias('SecureRandom', \OCP\Security\ISecureRandom::class); $this->registerService(\OCP\Security\ICrypto::class, function (Server $c) { return new Crypto($c->getConfig(), $c->getSecureRandom()); }); $this->registerAlias('Crypto', \OCP\Security\ICrypto::class); $this->registerService(\OCP\Security\IHasher::class, function (Server $c) { return new Hasher($c->getConfig()); }); $this->registerAlias('Hasher', \OCP\Security\IHasher::class); $this->registerService(\OCP\Security\ICredentialsManager::class, function (Server $c) { return new CredentialsManager($c->getCrypto(), $c->getDatabaseConnection()); }); $this->registerAlias('CredentialsManager', \OCP\Security\ICredentialsManager::class); $this->registerService(IDBConnection::class, function (Server $c) { $systemConfig = $c->getSystemConfig(); $factory = new \OC\DB\ConnectionFactory($systemConfig); $type = $systemConfig->getValue('dbtype', 'sqlite'); if (!$factory->isValidType($type)) { throw new \OC\DatabaseException('Invalid database type'); } $connectionParams = $factory->createConnectionParams(); $connection = $factory->getConnection($type, $connectionParams); $connection->getConfiguration()->setSQLLogger($c->getQueryLogger()); return $connection; }); $this->registerAlias('DatabaseConnection', IDBConnection::class); $this->registerService('HTTPHelper', function (Server $c) { $config = $c->getConfig(); return new HTTPHelper( $config, $c->getHTTPClientService() ); }); $this->registerService(\OCP\Http\Client\IClientService::class, function (Server $c) { $user = \OC_User::getUser(); $uid = $user ? $user : null; return new ClientService( $c->getConfig(), new \OC\Security\CertificateManager( $uid, new View(), $c->getConfig(), $c->getLogger(), $c->getSecureRandom() ) ); }); $this->registerAlias('HttpClientService', \OCP\Http\Client\IClientService::class); $this->registerService(\OCP\Diagnostics\IEventLogger::class, function (Server $c) { $eventLogger = new EventLogger(); if ($c->getSystemConfig()->getValue('debug', false)) { // In debug mode, module is being activated by default $eventLogger->activate(); } return $eventLogger; }); $this->registerAlias('EventLogger', \OCP\Diagnostics\IEventLogger::class); $this->registerService(\OCP\Diagnostics\IQueryLogger::class, function (Server $c) { $queryLogger = new QueryLogger(); if ($c->getSystemConfig()->getValue('debug', false)) { // In debug mode, module is being activated by default $queryLogger->activate(); } return $queryLogger; }); $this->registerAlias('QueryLogger', \OCP\Diagnostics\IQueryLogger::class); $this->registerService(TempManager::class, function (Server $c) { return new TempManager( $c->getLogger(), $c->getConfig() ); }); $this->registerAlias('TempManager', TempManager::class); $this->registerAlias(ITempManager::class, TempManager::class); $this->registerService(AppManager::class, function (Server $c) { return new \OC\App\AppManager( $c->getUserSession(), $c->getAppConfig(), $c->getGroupManager(), $c->getMemCacheFactory(), $c->getEventDispatcher() ); }); $this->registerAlias('AppManager', AppManager::class); $this->registerAlias(IAppManager::class, AppManager::class); $this->registerService(\OCP\IDateTimeZone::class, function (Server $c) { return new DateTimeZone( $c->getConfig(), $c->getSession() ); }); $this->registerAlias('DateTimeZone', \OCP\IDateTimeZone::class); $this->registerService(\OCP\IDateTimeFormatter::class, function (Server $c) { $language = $c->getConfig()->getUserValue($c->getSession()->get('user_id'), 'core', 'lang', null); return new DateTimeFormatter( $c->getDateTimeZone()->getTimeZone(), $c->getL10N('lib', $language) ); }); $this->registerAlias('DateTimeFormatter', \OCP\IDateTimeFormatter::class); $this->registerService(\OCP\Files\Config\IUserMountCache::class, function (Server $c) { $mountCache = new UserMountCache($c->getDatabaseConnection(), $c->getUserManager(), $c->getLogger()); $listener = new UserMountCacheListener($mountCache); $listener->listen($c->getUserManager()); return $mountCache; }); $this->registerAlias('UserMountCache', \OCP\Files\Config\IUserMountCache::class); $this->registerService(\OCP\Files\Config\IMountProviderCollection::class, function (Server $c) { $loader = \OC\Files\Filesystem::getLoader(); $mountCache = $c->query('UserMountCache'); $manager = new \OC\Files\Config\MountProviderCollection($loader, $mountCache); // builtin providers $config = $c->getConfig(); $manager->registerProvider(new CacheMountProvider($config)); $manager->registerHomeProvider(new LocalHomeMountProvider()); $manager->registerHomeProvider(new ObjectHomeMountProvider($config)); return $manager; }); $this->registerAlias('MountConfigManager', \OCP\Files\Config\IMountProviderCollection::class); $this->registerService('IniWrapper', function ($c) { return new IniGetWrapper(); }); $this->registerService('AsyncCommandBus', function (Server $c) { $jobList = $c->getJobList(); return new AsyncBus($jobList); }); $this->registerService('TrustedDomainHelper', function ($c) { return new TrustedDomainHelper($this->getConfig()); }); $this->registerService('Throttler', function(Server $c) { return new Throttler( $c->getDatabaseConnection(), new TimeFactory(), $c->getLogger(), $c->getConfig() ); }); $this->registerService('IntegrityCodeChecker', function (Server $c) { // IConfig and IAppManager requires a working database. This code // might however be called when ownCloud is not yet setup. if(\OC::$server->getSystemConfig()->getValue('installed', false)) { $config = $c->getConfig(); $appManager = $c->getAppManager(); } else { $config = null; $appManager = null; } return new Checker( new EnvironmentHelper(), new FileAccessHelper(), new AppLocator(), $config, $c->getMemCacheFactory(), $appManager, $c->getTempManager() ); }); $this->registerService(\OCP\IRequest::class, function ($c) { if (isset($this['urlParams'])) { $urlParams = $this['urlParams']; } else { $urlParams = []; } if (defined('PHPUNIT_RUN') && PHPUNIT_RUN && in_array('fakeinput', stream_get_wrappers()) ) { $stream = 'fakeinput://data'; } else { $stream = 'php://input'; } return new Request( [ 'get' => $_GET, 'post' => $_POST, 'files' => $_FILES, 'server' => $_SERVER, 'env' => $_ENV, 'cookies' => $_COOKIE, 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) ? $_SERVER['REQUEST_METHOD'] : null, 'urlParams' => $urlParams, ], $this->getSecureRandom(), $this->getConfig(), $this->getCsrfTokenManager(), $stream ); }); $this->registerAlias('Request', \OCP\IRequest::class); $this->registerService(\OCP\Mail\IMailer::class, function (Server $c) { return new Mailer( $c->getConfig(), $c->getLogger(), $c->query(Defaults::class), $c->getURLGenerator(), $c->getL10N('lib') ); }); $this->registerAlias('Mailer', \OCP\Mail\IMailer::class); $this->registerService('LDAPProvider', function(Server $c) { $config = $c->getConfig(); $factoryClass = $config->getSystemValue('ldapProviderFactory', null); if(is_null($factoryClass)) { throw new \Exception('ldapProviderFactory not set'); } /** @var \OCP\LDAP\ILDAPProviderFactory $factory */ $factory = new $factoryClass($this); return $factory->getLDAPProvider(); }); $this->registerService('LockingProvider', function (Server $c) { $ini = $c->getIniWrapper(); $config = $c->getConfig(); $ttl = $config->getSystemValue('filelocking.ttl', max(3600, $ini->getNumeric('max_execution_time'))); if ($config->getSystemValue('filelocking.enabled', true) or (defined('PHPUNIT_RUN') && PHPUNIT_RUN)) { /** @var \OC\Memcache\Factory $memcacheFactory */ $memcacheFactory = $c->getMemCacheFactory(); $memcache = $memcacheFactory->createLocking('lock'); if (!($memcache instanceof \OC\Memcache\NullCache)) { return new MemcacheLockingProvider($memcache, $ttl); } return new DBLockingProvider($c->getDatabaseConnection(), $c->getLogger(), new TimeFactory(), $ttl); } return new NoopLockingProvider(); }); $this->registerService(\OCP\Files\Mount\IMountManager::class, function () { return new \OC\Files\Mount\Manager(); }); $this->registerAlias('MountManager', \OCP\Files\Mount\IMountManager::class); $this->registerService(\OCP\Files\IMimeTypeDetector::class, function (Server $c) { return new \OC\Files\Type\Detection( $c->getURLGenerator(), \OC::$configDir, \OC::$SERVERROOT . '/resources/config/' ); }); $this->registerAlias('MimeTypeDetector', \OCP\Files\IMimeTypeDetector::class); $this->registerService(\OCP\Files\IMimeTypeLoader::class, function (Server $c) { return new \OC\Files\Type\Loader( $c->getDatabaseConnection() ); }); $this->registerAlias('MimeTypeLoader', \OCP\Files\IMimeTypeLoader::class); $this->registerService(BundleFetcher::class, function () { return new BundleFetcher($this->getL10N('lib')); }); $this->registerService(\OCP\Notification\IManager::class, function (Server $c) { return new Manager( $c->query(IValidator::class) ); }); $this->registerAlias('NotificationManager', \OCP\Notification\IManager::class); $this->registerService(\OC\CapabilitiesManager::class, function (Server $c) { $manager = new \OC\CapabilitiesManager($c->getLogger()); $manager->registerCapability(function () use ($c) { return new \OC\OCS\CoreCapabilities($c->getConfig()); }); return $manager; }); $this->registerAlias('CapabilitiesManager', \OC\CapabilitiesManager::class); $this->registerService(\OCP\Comments\ICommentsManager::class, function(Server $c) { $config = $c->getConfig(); $factoryClass = $config->getSystemValue('comments.managerFactory', '\OC\Comments\ManagerFactory'); /** @var \OCP\Comments\ICommentsManagerFactory $factory */ $factory = new $factoryClass($this); return $factory->getManager(); }); $this->registerAlias('CommentsManager', \OCP\Comments\ICommentsManager::class); $this->registerService('ThemingDefaults', function(Server $c) { /* * Dark magic for autoloader. * If we do a class_exists it will try to load the class which will * make composer cache the result. Resulting in errors when enabling * the theming app. */ $prefixes = \OC::$composerAutoloader->getPrefixesPsr4(); if (isset($prefixes['OCA\\Theming\\'])) { $classExists = true; } else { $classExists = false; } if ($classExists && $c->getConfig()->getSystemValue('installed', false) && $c->getAppManager()->isInstalled('theming') && $c->getTrustedDomainHelper()->isTrustedDomain($c->getRequest()->getInsecureServerHost())) { return new ThemingDefaults( $c->getConfig(), $c->getL10N('theming'), $c->getURLGenerator(), $c->getAppDataDir('theming'), $c->getMemCacheFactory(), new Util($c->getConfig(), $this->getAppManager(), $this->getAppDataDir('theming')) ); } return new \OC_Defaults(); }); $this->registerService(SCSSCacher::class, function(Server $c) { /** @var Factory $cacheFactory */ $cacheFactory = $c->query(Factory::class); return new SCSSCacher( $c->getLogger(), $c->query(\OC\Files\AppData\Factory::class), $c->getURLGenerator(), $c->getConfig(), $c->getThemingDefaults(), \OC::$SERVERROOT, $cacheFactory->create('SCSS') ); }); $this->registerService(EventDispatcher::class, function () { return new EventDispatcher(); }); $this->registerAlias('EventDispatcher', EventDispatcher::class); $this->registerAlias(EventDispatcherInterface::class, EventDispatcher::class); $this->registerService('CryptoWrapper', function (Server $c) { // FIXME: Instantiiated here due to cyclic dependency $request = new Request( [ 'get' => $_GET, 'post' => $_POST, 'files' => $_FILES, 'server' => $_SERVER, 'env' => $_ENV, 'cookies' => $_COOKIE, 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) ? $_SERVER['REQUEST_METHOD'] : null, ], $c->getSecureRandom(), $c->getConfig() ); return new CryptoWrapper( $c->getConfig(), $c->getCrypto(), $c->getSecureRandom(), $request ); }); $this->registerService('CsrfTokenManager', function (Server $c) { $tokenGenerator = new CsrfTokenGenerator($c->getSecureRandom()); return new CsrfTokenManager( $tokenGenerator, $c->query(SessionStorage::class) ); }); $this->registerService(SessionStorage::class, function (Server $c) { return new SessionStorage($c->getSession()); }); $this->registerService(\OCP\Security\IContentSecurityPolicyManager::class, function (Server $c) { return new ContentSecurityPolicyManager(); }); $this->registerAlias('ContentSecurityPolicyManager', \OCP\Security\IContentSecurityPolicyManager::class); $this->registerService('ContentSecurityPolicyNonceManager', function(Server $c) { return new ContentSecurityPolicyNonceManager( $c->getCsrfTokenManager(), $c->getRequest() ); }); $this->registerService(\OCP\Share\IManager::class, function(Server $c) { $config = $c->getConfig(); $factoryClass = $config->getSystemValue('sharing.managerFactory', '\OC\Share20\ProviderFactory'); /** @var \OCP\Share\IProviderFactory $factory */ $factory = new $factoryClass($this); $manager = new \OC\Share20\Manager( $c->getLogger(), $c->getConfig(), $c->getSecureRandom(), $c->getHasher(), $c->getMountManager(), $c->getGroupManager(), $c->getL10N('lib'), $c->getL10NFactory(), $factory, $c->getUserManager(), $c->getLazyRootFolder(), $c->getEventDispatcher(), $c->getMailer(), $c->getURLGenerator(), $c->getThemingDefaults() ); return $manager; }); $this->registerAlias('ShareManager', \OCP\Share\IManager::class); $this->registerService('SettingsManager', function(Server $c) { $manager = new \OC\Settings\Manager( $c->getLogger(), $c->getDatabaseConnection(), $c->getL10N('lib'), $c->getConfig(), $c->getEncryptionManager(), $c->getUserManager(), $c->getLockingProvider(), $c->getRequest(), new \OC\Settings\Mapper($c->getDatabaseConnection()), $c->getURLGenerator() ); return $manager; }); $this->registerService(\OC\Files\AppData\Factory::class, function (Server $c) { return new \OC\Files\AppData\Factory( $c->getRootFolder(), $c->getSystemConfig() ); }); $this->registerService('LockdownManager', function (Server $c) { return new LockdownManager(function() use ($c) { return $c->getSession(); }); }); $this->registerService(\OCP\OCS\IDiscoveryService::class, function (Server $c) { return new DiscoveryService($c->getMemCacheFactory(), $c->getHTTPClientService()); }); $this->registerService(ICloudIdManager::class, function (Server $c) { return new CloudIdManager(); }); /* To trick DI since we don't extend the DIContainer here */ $this->registerService(CleanPreviewsBackgroundJob::class, function (Server $c) { return new CleanPreviewsBackgroundJob( $c->getRootFolder(), $c->getLogger(), $c->getJobList(), new TimeFactory() ); }); $this->registerAlias(\OCP\AppFramework\Utility\IControllerMethodReflector::class, \OC\AppFramework\Utility\ControllerMethodReflector::class); $this->registerAlias('ControllerMethodReflector', \OCP\AppFramework\Utility\IControllerMethodReflector::class); $this->registerAlias(\OCP\AppFramework\Utility\ITimeFactory::class, \OC\AppFramework\Utility\TimeFactory::class); $this->registerAlias('TimeFactory', \OCP\AppFramework\Utility\ITimeFactory::class); $this->registerService(Defaults::class, function (Server $c) { return new Defaults( $c->getThemingDefaults() ); }); $this->registerAlias('Defaults', \OCP\Defaults::class); $this->registerService(\OCP\ISession::class, function(SimpleContainer $c) { return $c->query(\OCP\IUserSession::class)->getSession(); }); $this->registerService(IShareHelper::class, function(Server $c) { return new ShareHelper( $c->query(\OCP\Share\IManager::class) ); }); } /** * @return \OCP\Contacts\IManager */ public function getContactsManager() { return $this->query('ContactsManager'); } /** * @return \OC\Encryption\Manager */ public function getEncryptionManager() { return $this->query('EncryptionManager'); } /** * @return \OC\Encryption\File */ public function getEncryptionFilesHelper() { return $this->query('EncryptionFileHelper'); } /** * @return \OCP\Encryption\Keys\IStorage */ public function getEncryptionKeyStorage() { return $this->query('EncryptionKeyStorage'); } /** * The current request object holding all information about the request * currently being processed is returned from this method. * In case the current execution was not initiated by a web request null is returned * * @return \OCP\IRequest */ public function getRequest() { return $this->query('Request'); } /** * Returns the preview manager which can create preview images for a given file * * @return \OCP\IPreview */ public function getPreviewManager() { return $this->query('PreviewManager'); } /** * Returns the tag manager which can get and set tags for different object types * * @see \OCP\ITagManager::load() * @return \OCP\ITagManager */ public function getTagManager() { return $this->query('TagManager'); } /** * Returns the system-tag manager * * @return \OCP\SystemTag\ISystemTagManager * * @since 9.0.0 */ public function getSystemTagManager() { return $this->query('SystemTagManager'); } /** * Returns the system-tag object mapper * * @return \OCP\SystemTag\ISystemTagObjectMapper * * @since 9.0.0 */ public function getSystemTagObjectMapper() { return $this->query('SystemTagObjectMapper'); } /** * Returns the avatar manager, used for avatar functionality * * @return \OCP\IAvatarManager */ public function getAvatarManager() { return $this->query('AvatarManager'); } /** * Returns the root folder of ownCloud's data directory * * @return \OCP\Files\IRootFolder */ public function getRootFolder() { return $this->query('LazyRootFolder'); } /** * Returns the root folder of ownCloud's data directory * This is the lazy variant so this gets only initialized once it * is actually used. * * @return \OCP\Files\IRootFolder */ public function getLazyRootFolder() { return $this->query('LazyRootFolder'); } /** * Returns a view to ownCloud's files folder * * @param string $userId user ID * @return \OCP\Files\Folder|null */ public function getUserFolder($userId = null) { if ($userId === null) { $user = $this->getUserSession()->getUser(); if (!$user) { return null; } $userId = $user->getUID(); } $root = $this->getRootFolder(); return $root->getUserFolder($userId); } /** * Returns an app-specific view in ownClouds data directory * * @return \OCP\Files\Folder * @deprecated since 9.2.0 use IAppData */ public function getAppFolder() { $dir = '/' . \OC_App::getCurrentApp(); $root = $this->getRootFolder(); if (!$root->nodeExists($dir)) { $folder = $root->newFolder($dir); } else { $folder = $root->get($dir); } return $folder; } /** * @return \OC\User\Manager */ public function getUserManager() { return $this->query('UserManager'); } /** * @return \OC\Group\Manager */ public function getGroupManager() { return $this->query('GroupManager'); } /** * @return \OC\User\Session */ public function getUserSession() { return $this->query('UserSession'); } /** * @return \OCP\ISession */ public function getSession() { return $this->query('UserSession')->getSession(); } /** * @param \OCP\ISession $session */ public function setSession(\OCP\ISession $session) { $this->query(SessionStorage::class)->setSession($session); $this->query('UserSession')->setSession($session); $this->query(Store::class)->setSession($session); } /** * @return \OC\Authentication\TwoFactorAuth\Manager */ public function getTwoFactorAuthManager() { return $this->query('\OC\Authentication\TwoFactorAuth\Manager'); } /** * @return \OC\NavigationManager */ public function getNavigationManager() { return $this->query('NavigationManager'); } /** * @return \OCP\IConfig */ public function getConfig() { return $this->query('AllConfig'); } /** * @internal For internal use only * @return \OC\SystemConfig */ public function getSystemConfig() { return $this->query('SystemConfig'); } /** * Returns the app config manager * * @return \OCP\IAppConfig */ public function getAppConfig() { return $this->query('AppConfig'); } /** * @return \OCP\L10N\IFactory */ public function getL10NFactory() { return $this->query('L10NFactory'); } /** * get an L10N instance * * @param string $app appid * @param string $lang * @return IL10N */ public function getL10N($app, $lang = null) { return $this->getL10NFactory()->get($app, $lang); } /** * @return \OCP\IURLGenerator */ public function getURLGenerator() { return $this->query('URLGenerator'); } /** * @return \OCP\IHelper */ public function getHelper() { return $this->query('AppHelper'); } /** * @return AppFetcher */ public function getAppFetcher() { return $this->query(AppFetcher::class); } /** * Returns an ICache instance. Since 8.1.0 it returns a fake cache. Use * getMemCacheFactory() instead. * * @return \OCP\ICache * @deprecated 8.1.0 use getMemCacheFactory to obtain a proper cache */ public function getCache() { return $this->query('UserCache'); } /** * Returns an \OCP\CacheFactory instance * * @return \OCP\ICacheFactory */ public function getMemCacheFactory() { return $this->query('MemCacheFactory'); } /** * Returns an \OC\RedisFactory instance * * @return \OC\RedisFactory */ public function getGetRedisFactory() { return $this->query('RedisFactory'); } /** * Returns the current session * * @return \OCP\IDBConnection */ public function getDatabaseConnection() { return $this->query('DatabaseConnection'); } /** * Returns the activity manager * * @return \OCP\Activity\IManager */ public function getActivityManager() { return $this->query('ActivityManager'); } /** * Returns an job list for controlling background jobs * * @return \OCP\BackgroundJob\IJobList */ public function getJobList() { return $this->query('JobList'); } /** * Returns a logger instance * * @return \OCP\ILogger */ public function getLogger() { return $this->query('Logger'); } /** * Returns a router for generating and matching urls * * @return \OCP\Route\IRouter */ public function getRouter() { return $this->query('Router'); } /** * Returns a search instance * * @return \OCP\ISearch */ public function getSearch() { return $this->query('Search'); } /** * Returns a SecureRandom instance * * @return \OCP\Security\ISecureRandom */ public function getSecureRandom() { return $this->query('SecureRandom'); } /** * Returns a Crypto instance * * @return \OCP\Security\ICrypto */ public function getCrypto() { return $this->query('Crypto'); } /** * Returns a Hasher instance * * @return \OCP\Security\IHasher */ public function getHasher() { return $this->query('Hasher'); } /** * Returns a CredentialsManager instance * * @return \OCP\Security\ICredentialsManager */ public function getCredentialsManager() { return $this->query('CredentialsManager'); } /** * Returns an instance of the HTTP helper class * * @deprecated Use getHTTPClientService() * @return \OC\HTTPHelper */ public function getHTTPHelper() { return $this->query('HTTPHelper'); } /** * Get the certificate manager for the user * * @param string $userId (optional) if not specified the current loggedin user is used, use null to get the system certificate manager * @return \OCP\ICertificateManager | null if $uid is null and no user is logged in */ public function getCertificateManager($userId = '') { if ($userId === '') { $userSession = $this->getUserSession(); $user = $userSession->getUser(); if (is_null($user)) { return null; } $userId = $user->getUID(); } return new CertificateManager( $userId, new View(), $this->getConfig(), $this->getLogger(), $this->getSecureRandom() ); } /** * Returns an instance of the HTTP client service * * @return \OCP\Http\Client\IClientService */ public function getHTTPClientService() { return $this->query('HttpClientService'); } /** * Create a new event source * * @return \OCP\IEventSource */ public function createEventSource() { return new \OC_EventSource(); } /** * Get the active event logger * * The returned logger only logs data when debug mode is enabled * * @return \OCP\Diagnostics\IEventLogger */ public function getEventLogger() { return $this->query('EventLogger'); } /** * Get the active query logger * * The returned logger only logs data when debug mode is enabled * * @return \OCP\Diagnostics\IQueryLogger */ public function getQueryLogger() { return $this->query('QueryLogger'); } /** * Get the manager for temporary files and folders * * @return \OCP\ITempManager */ public function getTempManager() { return $this->query('TempManager'); } /** * Get the app manager * * @return \OCP\App\IAppManager */ public function getAppManager() { return $this->query('AppManager'); } /** * Creates a new mailer * * @return \OCP\Mail\IMailer */ public function getMailer() { return $this->query('Mailer'); } /** * Get the webroot * * @return string */ public function getWebRoot() { return $this->webRoot; } /** * @return \OC\OCSClient */ public function getOcsClient() { return $this->query('OcsClient'); } /** * @return \OCP\IDateTimeZone */ public function getDateTimeZone() { return $this->query('DateTimeZone'); } /** * @return \OCP\IDateTimeFormatter */ public function getDateTimeFormatter() { return $this->query('DateTimeFormatter'); } /** * @return \OCP\Files\Config\IMountProviderCollection */ public function getMountProviderCollection() { return $this->query('MountConfigManager'); } /** * Get the IniWrapper * * @return IniGetWrapper */ public function getIniWrapper() { return $this->query('IniWrapper'); } /** * @return \OCP\Command\IBus */ public function getCommandBus() { return $this->query('AsyncCommandBus'); } /** * Get the trusted domain helper * * @return TrustedDomainHelper */ public function getTrustedDomainHelper() { return $this->query('TrustedDomainHelper'); } /** * Get the locking provider * * @return \OCP\Lock\ILockingProvider * @since 8.1.0 */ public function getLockingProvider() { return $this->query('LockingProvider'); } /** * @return \OCP\Files\Mount\IMountManager **/ function getMountManager() { return $this->query('MountManager'); } /** @return \OCP\Files\Config\IUserMountCache */ function getUserMountCache() { return $this->query('UserMountCache'); } /** * Get the MimeTypeDetector * * @return \OCP\Files\IMimeTypeDetector */ public function getMimeTypeDetector() { return $this->query('MimeTypeDetector'); } /** * Get the MimeTypeLoader * * @return \OCP\Files\IMimeTypeLoader */ public function getMimeTypeLoader() { return $this->query('MimeTypeLoader'); } /** * Get the manager of all the capabilities * * @return \OC\CapabilitiesManager */ public function getCapabilitiesManager() { return $this->query('CapabilitiesManager'); } /** * Get the EventDispatcher * * @return EventDispatcherInterface * @since 8.2.0 */ public function getEventDispatcher() { return $this->query('EventDispatcher'); } /** * Get the Notification Manager * * @return \OCP\Notification\IManager * @since 8.2.0 */ public function getNotificationManager() { return $this->query('NotificationManager'); } /** * @return \OCP\Comments\ICommentsManager */ public function getCommentsManager() { return $this->query('CommentsManager'); } /** * @return \OCA\Theming\ThemingDefaults */ public function getThemingDefaults() { return $this->query('ThemingDefaults'); } /** * @return \OC\IntegrityCheck\Checker */ public function getIntegrityCodeChecker() { return $this->query('IntegrityCodeChecker'); } /** * @return \OC\Session\CryptoWrapper */ public function getSessionCryptoWrapper() { return $this->query('CryptoWrapper'); } /** * @return CsrfTokenManager */ public function getCsrfTokenManager() { return $this->query('CsrfTokenManager'); } /** * @return Throttler */ public function getBruteForceThrottler() { return $this->query('Throttler'); } /** * @return IContentSecurityPolicyManager */ public function getContentSecurityPolicyManager() { return $this->query('ContentSecurityPolicyManager'); } /** * @return ContentSecurityPolicyNonceManager */ public function getContentSecurityPolicyNonceManager() { return $this->query('ContentSecurityPolicyNonceManager'); } /** * Not a public API as of 8.2, wait for 9.0 * * @return \OCA\Files_External\Service\BackendService */ public function getStoragesBackendService() { return $this->query('OCA\\Files_External\\Service\\BackendService'); } /** * Not a public API as of 8.2, wait for 9.0 * * @return \OCA\Files_External\Service\GlobalStoragesService */ public function getGlobalStoragesService() { return $this->query('OCA\\Files_External\\Service\\GlobalStoragesService'); } /** * Not a public API as of 8.2, wait for 9.0 * * @return \OCA\Files_External\Service\UserGlobalStoragesService */ public function getUserGlobalStoragesService() { return $this->query('OCA\\Files_External\\Service\\UserGlobalStoragesService'); } /** * Not a public API as of 8.2, wait for 9.0 * * @return \OCA\Files_External\Service\UserStoragesService */ public function getUserStoragesService() { return $this->query('OCA\\Files_External\\Service\\UserStoragesService'); } /** * @return \OCP\Share\IManager */ public function getShareManager() { return $this->query('ShareManager'); } /** * Returns the LDAP Provider * * @return \OCP\LDAP\ILDAPProvider */ public function getLDAPProvider() { return $this->query('LDAPProvider'); } /** * @return \OCP\Settings\IManager */ public function getSettingsManager() { return $this->query('SettingsManager'); } /** * @return \OCP\Files\IAppData */ public function getAppDataDir($app) { /** @var \OC\Files\AppData\Factory $factory */ $factory = $this->query(\OC\Files\AppData\Factory::class); return $factory->get($app); } /** * @return \OCP\Lockdown\ILockdownManager */ public function getLockdownManager() { return $this->query('LockdownManager'); } /** * @return \OCP\Federation\ICloudIdManager */ public function getCloudIdManager() { return $this->query(ICloudIdManager::class); } } private/SystemConfig.php 0000604 00000010024 15247130453 0011336 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OCP\IConfig; /** * Class which provides access to the system config values stored in config.php * Internal class for bootstrap only. * fixes cyclic DI: AllConfig needs AppConfig needs Database needs AllConfig */ class SystemConfig { /** @var array */ protected $sensitiveValues = [ 'dbpassword' => true, 'dbuser' => true, 'mail_smtpname' => true, 'mail_smtppassword' => true, 'passwordsalt' => true, 'secret' => true, 'updater.secret' => true, 'proxyuserpwd' => true, 'log.condition' => [ 'shared_secret' => true, ], 'license-key' => true, 'redis' => [ 'password' => true, ], 'objectstore' => [ 'arguments' => [ 'password' => true, 'options' => [ 'credentials' => [ 'key' => true, 'secret' => true, ] ] ], ], ]; /** @var Config */ private $config; public function __construct(Config $config) { $this->config = $config; } /** * Lists all available config keys * @return array an array of key names */ public function getKeys() { return $this->config->getKeys(); } /** * Sets a new system wide value * * @param string $key the key of the value, under which will be saved * @param mixed $value the value that should be stored */ public function setValue($key, $value) { $this->config->setValue($key, $value); } /** * Sets and deletes values and writes the config.php * * @param array $configs Associative array with `key => value` pairs * If value is null, the config key will be deleted */ public function setValues(array $configs) { $this->config->setValues($configs); } /** * Looks up a system wide defined value * * @param string $key the key of the value, under which it was saved * @param mixed $default the default value to be returned if the value isn't set * @return mixed the value or $default */ public function getValue($key, $default = '') { return $this->config->getValue($key, $default); } /** * Looks up a system wide defined value and filters out sensitive data * * @param string $key the key of the value, under which it was saved * @param mixed $default the default value to be returned if the value isn't set * @return mixed the value or $default */ public function getFilteredValue($key, $default = '') { $value = $this->getValue($key, $default); if (isset($this->sensitiveValues[$key])) { $value = $this->removeSensitiveValue($this->sensitiveValues[$key], $value); } return $value; } /** * Delete a system wide defined value * * @param string $key the key of the value, under which it was saved */ public function deleteValue($key) { $this->config->deleteKey($key); } /** * @param bool|array $keysToRemove * @param mixed $value * @return mixed */ protected function removeSensitiveValue($keysToRemove, $value) { if ($keysToRemove === true) { return IConfig::SENSITIVE_VALUE; } if (is_array($value)) { foreach ($keysToRemove as $keyToRemove => $valueToRemove) { if (isset($value[$keyToRemove])) { $value[$keyToRemove] = $this->removeSensitiveValue($valueToRemove, $value[$keyToRemove]); } } } return $value; } } private/Diagnostics/Event.php 0000604 00000003544 15247130453 0012265 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Diagnostics; use OCP\Diagnostics\IEvent; class Event implements IEvent { /** * @var string */ protected $id; /** * @var float */ protected $start; /** * @var float */ protected $end; /** * @var string */ protected $description; /** * @param string $id * @param string $description * @param float $start */ public function __construct($id, $description, $start) { $this->id = $id; $this->description = $description; $this->start = $start; } /** * @param float $time */ public function end($time) { $this->end = $time; } /** * @return float */ public function getStart() { return $this->start; } /** * @return string */ public function getId() { return $this->id; } /** * @return string */ public function getDescription() { return $this->description; } /** * @return float */ public function getEnd() { return $this->end; } /** * @return float */ public function getDuration() { if (!$this->end) { $this->end = microtime(true); } return $this->end - $this->start; } } private/Diagnostics/EventLogger.php 0000604 00000003572 15247130453 0013426 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Piotr Mrowczynski <piotr@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Diagnostics; use OCP\Diagnostics\IEventLogger; class EventLogger implements IEventLogger { /** * @var \OC\Diagnostics\Event[] */ private $events = []; /** * @var bool - Module needs to be activated by some app */ private $activated = false; /** * @inheritdoc */ public function start($id, $description) { if ($this->activated){ $this->events[$id] = new Event($id, $description, microtime(true)); } } /** * @inheritdoc */ public function end($id) { if ($this->activated && isset($this->events[$id])) { $timing = $this->events[$id]; $timing->end(microtime(true)); } } /** * @inheritdoc */ public function log($id, $description, $start, $end) { if ($this->activated) { $this->events[$id] = new Event($id, $description, $start); $this->events[$id]->end($end); } } /** * @inheritdoc */ public function getEvents() { return $this->events; } /** * @inheritdoc */ public function activate() { $this->activated = true; } } private/Diagnostics/Query.php 0000604 00000003337 15247130453 0012311 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Diagnostics; use OCP\Diagnostics\IQuery; class Query implements IQuery { private $sql; private $params; private $start; private $end; private $stack; /** * @param string $sql * @param array $params * @param int $start */ public function __construct($sql, $params, $start, array $stack) { $this->sql = $sql; $this->params = $params; $this->start = $start; $this->stack = $stack; } public function end($time) { $this->end = $time; } /** * @return array */ public function getParams() { return $this->params; } /** * @return string */ public function getSql() { return $this->sql; } /** * @return float */ public function getStart() { return $this->start; } /** * @return float */ public function getDuration() { return $this->end - $this->start; } public function getStartTime() { return $this->start; } public function getStacktrace() { return $this->stack; } } private/Diagnostics/QueryLogger.php 0000604 00000004207 15247130453 0013446 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Piotr Mrowczynski <piotr@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Diagnostics; use OC\Cache\CappedMemoryCache; use OCP\Diagnostics\IQueryLogger; class QueryLogger implements IQueryLogger { /** * @var \OC\Diagnostics\Query */ protected $activeQuery; /** * @var \OC\Diagnostics\Query[] */ protected $queries; /** * QueryLogger constructor. */ public function __construct() { $this->queries = new CappedMemoryCache(1024); } /** * @var bool - Module needs to be activated by some app */ private $activated = false; /** * @inheritdoc */ public function startQuery($sql, array $params = null, array $types = null) { if ($this->activated) { $this->activeQuery = new Query($sql, $params, microtime(true), $this->getStack()); } } private function getStack() { $stack = debug_backtrace(); array_shift($stack); array_shift($stack); array_shift($stack); return $stack; } /** * @inheritdoc */ public function stopQuery() { if ($this->activated && $this->activeQuery) { $this->activeQuery->end(microtime(true)); $this->queries[] = $this->activeQuery; $this->activeQuery = null; } } /** * @inheritdoc */ public function getQueries() { return $this->queries->getData(); } /** * @inheritdoc */ public function activate() { $this->activated = true; } } private/Search/Provider/File.php 0000604 00000004153 15247130453 0012610 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Search\Provider; use OC\Files\Filesystem; /** * Provide search results from the 'files' app */ class File extends \OCP\Search\Provider { /** * Search for files and folders matching the given query * @param string $query * @return \OCP\Search\Result */ function search($query) { $files = Filesystem::search($query); $results = array(); // edit results foreach ($files as $fileData) { // skip versions if (strpos($fileData['path'], '_versions') === 0) { continue; } // skip top-level folder if ($fileData['name'] === 'files' && $fileData['parent'] === -1) { continue; } // create audio result if($fileData['mimepart'] === 'audio'){ $result = new \OC\Search\Result\Audio($fileData); } // create image result elseif($fileData['mimepart'] === 'image'){ $result = new \OC\Search\Result\Image($fileData); } // create folder result elseif($fileData['mimetype'] === 'httpd/unix-directory'){ $result = new \OC\Search\Result\Folder($fileData); } // or create file result else{ $result = new \OC\Search\Result\File($fileData); } // add to results $results[] = $result; } // return return $results; } } private/Search/Result/Folder.php 0000604 00000002007 15247130453 0012624 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Search\Result; /** * A found folder */ class Folder extends File { /** * Type name; translated in templates * @var string */ public $type = 'folder'; } private/Search/Result/File.php 0000604 00000005135 15247130453 0012275 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Search\Result; use OCP\Files\FileInfo; use OCP\Files\Folder; /** * A found file */ class File extends \OCP\Search\Result { /** * Type name; translated in templates * @var string */ public $type = 'file'; /** * Path to file * @var string */ public $path; /** * Size, in bytes * @var int */ public $size; /** * Date modified, in human readable form * @var string */ public $modified; /** * File mime type * @var string */ public $mime_type; /** * File permissions: * * @var string */ public $permissions; /** * Create a new file search result * @param FileInfo $data file data given by provider */ public function __construct(FileInfo $data) { $path = $this->getRelativePath($data->getPath()); $info = pathinfo($path); $this->id = $data->getId(); $this->name = $info['basename']; $this->link = \OC::$server->getURLGenerator()->linkToRoute( 'files.view.index', [ 'dir' => $info['dirname'], 'scrollto' => $info['basename'], ] ); $this->permissions = $data->getPermissions(); $this->path = $path; $this->size = $data->getSize(); $this->modified = $data->getMtime(); $this->mime = $data->getMimetype(); } /** * @var Folder $userFolderCache */ static protected $userFolderCache = null; /** * converts a path relative to the users files folder * eg /user/files/foo.txt -> /foo.txt * @param string $path * @return string relative path */ protected function getRelativePath ($path) { if (!isset(self::$userFolderCache)) { $user = \OC::$server->getUserSession()->getUser()->getUID(); self::$userFolderCache = \OC::$server->getUserFolder($user); } return self::$userFolderCache->getRelativePath($path); } } private/Search/Result/Audio.php 0000604 00000002061 15247130453 0012452 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Search\Result; /** * A found audio file */ class Audio extends File { /** * Type name; translated in templates * @var string */ public $type = 'audio'; /** * @TODO add ID3 information */ } private/Search/Result/Image.php 0000604 00000002062 15247130453 0012434 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andrew Brown <andrew@casabrown.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Search\Result; /** * A found image file */ class Image extends File { /** * Type name; translated in templates * @var string */ public $type = 'image'; /** * @TODO add EXIF information */ } private/Command/QueueBus.php 0000604 00000003507 15247130453 0012050 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Command; use OCP\Command\IBus; use OCP\Command\ICommand; class QueueBus implements IBus { /** * @var (ICommand|callable)[] */ private $queue = []; /** * Schedule a command to be fired * * @param \OCP\Command\ICommand | callable $command */ public function push($command) { $this->queue[] = $command; } /** * Require all commands using a trait to be run synchronous * * @param string $trait */ public function requireSync($trait) { } /** * @param \OCP\Command\ICommand | callable $command */ private function runCommand($command) { if ($command instanceof ICommand) { // ensure the command can be serialized $serialized = serialize($command); if(strlen($serialized) > 4000) { throw new \InvalidArgumentException('Trying to push a command which serialized form can not be stored in the database (>4000 character)'); } $unserialized = unserialize($serialized); $unserialized->handle(); } else { $command(); } } public function run() { while ($command = array_shift($this->queue)) { $this->runCommand($command); } } } private/Command/CallableJob.php 0000604 00000002071 15247130453 0012437 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Command; use OC\BackgroundJob\QueuedJob; class CallableJob extends QueuedJob { protected function run($serializedCallable) { $callable = unserialize($serializedCallable); if (is_callable($callable)) { $callable(); } else { throw new \InvalidArgumentException('Invalid serialized callable'); } } } private/Command/ClosureJob.php 0000604 00000002204 15247130453 0012352 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Command; use OC\BackgroundJob\QueuedJob; use SuperClosure\Serializer; class ClosureJob extends QueuedJob { protected function run($serializedCallable) { $serializer = new Serializer(); $callable = $serializer->unserialize($serializedCallable); if (is_callable($callable)) { $callable(); } else { throw new \InvalidArgumentException('Invalid serialized callable'); } } } private/Command/CommandJob.php 0000604 00000002225 15247130453 0012317 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Command; use OC\BackgroundJob\QueuedJob; use OCP\Command\ICommand; /** * Wrap a command in the background job interface */ class CommandJob extends QueuedJob { protected function run($serializedCommand) { $command = unserialize($serializedCommand); if ($command instanceof ICommand) { $command->handle(); } else { throw new \InvalidArgumentException('Invalid serialized command'); } } } private/Command/FileAccess.php 0000604 00000002005 15247130453 0012303 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Command; use OCP\IUser; trait FileAccess { protected function setupFS(IUser $user){ \OC_Util::setupFS($user->getUID()); } protected function getUserFolder(IUser $user) { $this->setupFS($user); return \OC::$server->getUserFolder($user->getUID()); } } private/Command/AsyncBus.php 0000604 00000006444 15247130453 0012044 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Command; use OCP\Command\IBus; use OCP\Command\ICommand; use SuperClosure\Serializer; /** * Asynchronous command bus that uses the background job system as backend */ class AsyncBus implements IBus { /** * @var \OCP\BackgroundJob\IJobList */ private $jobList; /** * List of traits for command which require sync execution * * @var string[] */ private $syncTraits = []; /** * @param \OCP\BackgroundJob\IJobList $jobList */ function __construct($jobList) { $this->jobList = $jobList; } /** * Schedule a command to be fired * * @param \OCP\Command\ICommand | callable $command */ public function push($command) { if ($this->canRunAsync($command)) { $this->jobList->add($this->getJobClass($command), $this->serializeCommand($command)); } else { $this->runCommand($command); } } /** * Require all commands using a trait to be run synchronous * * @param string $trait */ public function requireSync($trait) { $this->syncTraits[] = trim($trait, '\\'); } /** * @param \OCP\Command\ICommand | callable $command */ private function runCommand($command) { if ($command instanceof ICommand) { $command->handle(); } else { $command(); } } /** * @param \OCP\Command\ICommand | callable $command * @return string */ private function getJobClass($command) { if ($command instanceof \Closure) { return 'OC\Command\ClosureJob'; } else if (is_callable($command)) { return 'OC\Command\CallableJob'; } else if ($command instanceof ICommand) { return 'OC\Command\CommandJob'; } else { throw new \InvalidArgumentException('Invalid command'); } } /** * @param \OCP\Command\ICommand | callable $command * @return string */ private function serializeCommand($command) { if ($command instanceof \Closure) { $serializer = new Serializer(); return $serializer->serialize($command); } else if (is_callable($command) or $command instanceof ICommand) { return serialize($command); } else { throw new \InvalidArgumentException('Invalid command'); } } /** * @param \OCP\Command\ICommand | callable $command * @return bool */ private function canRunAsync($command) { $traits = $this->getTraits($command); foreach ($traits as $trait) { if (array_search($trait, $this->syncTraits) !== false) { return false; } } return true; } /** * @param \OCP\Command\ICommand | callable $command * @return string[] */ private function getTraits($command) { if ($command instanceof ICommand) { return class_uses($command); } else { return []; } } } private/Updater/VersionCheck.php 0000604 00000006640 15247130453 0012724 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Updater; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\Util; class VersionCheck { /** @var IClientService */ private $clientService; /** @var IConfig */ private $config; /** * @param IClientService $clientService * @param IConfig $config */ public function __construct(IClientService $clientService, IConfig $config) { $this->clientService = $clientService; $this->config = $config; } /** * Check if a new version is available * * @return array|bool */ public function check() { // Look up the cache - it is invalidated all 30 minutes if (((int)$this->config->getAppValue('core', 'lastupdatedat') + 1800) > time()) { return json_decode($this->config->getAppValue('core', 'lastupdateResult'), true); } $updaterUrl = $this->config->getSystemValue('updater.server.url', 'https://updates.nextcloud.com/updater_server/'); $this->config->setAppValue('core', 'lastupdatedat', time()); if ($this->config->getAppValue('core', 'installedat', '') === '') { $this->config->setAppValue('core', 'installedat', microtime(true)); } $version = Util::getVersion(); $version['installed'] = $this->config->getAppValue('core', 'installedat'); $version['updated'] = $this->config->getAppValue('core', 'lastupdatedat'); $version['updatechannel'] = \OC_Util::getChannel(); $version['edition'] = ''; $version['build'] = \OC_Util::getBuild(); $version['php_major'] = PHP_MAJOR_VERSION; $version['php_minor'] = PHP_MINOR_VERSION; $version['php_release'] = PHP_RELEASE_VERSION; $versionString = implode('x', $version); //fetch xml data from updater $url = $updaterUrl . '?version=' . $versionString; $tmp = []; try { $xml = $this->getUrlContent($url); } catch (\Exception $e) { return false; } if ($xml) { $loadEntities = libxml_disable_entity_loader(true); $data = @simplexml_load_string($xml); libxml_disable_entity_loader($loadEntities); if ($data !== false) { $tmp['version'] = (string)$data->version; $tmp['versionstring'] = (string)$data->versionstring; $tmp['url'] = (string)$data->url; $tmp['web'] = (string)$data->web; $tmp['autoupdater'] = (string)$data->autoupdater; } else { libxml_clear_errors(); } } else { $data = []; } // Cache the result $this->config->setAppValue('core', 'lastupdateResult', json_encode($data)); return $tmp; } /** * @codeCoverageIgnore * @param string $url * @return resource|string * @throws \Exception */ protected function getUrlContent($url) { $client = $this->clientService->newClient(); $response = $client->get($url); return $response->getBody(); } } private/Session/Internal.php 0000604 00000010345 15247130453 0012131 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author cetra3 <peter@parashift.com.au> * @author Christoph Wurst <christoph@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Phil Davis <phil.davis@inf.org> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Session; use OCP\Session\Exceptions\SessionNotAvailableException; /** * Class Internal * * wrap php's internal session handling into the Session interface * * @package OC\Session */ class Internal extends Session { /** * @param string $name * @throws \Exception */ public function __construct($name) { set_error_handler(array($this, 'trapError')); $this->invoke('session_name', [$name]); try { $this->invoke('session_start'); } catch (\Exception $e) { setcookie($this->invoke('session_name'), null, -1, \OC::$WEBROOT ?: '/'); } restore_error_handler(); if (!isset($_SESSION)) { throw new \Exception('Failed to start session'); } } /** * @param string $key * @param integer $value */ public function set($key, $value) { $this->validateSession(); $_SESSION[$key] = $value; } /** * @param string $key * @return mixed */ public function get($key) { if (!$this->exists($key)) { return null; } return $_SESSION[$key]; } /** * @param string $key * @return bool */ public function exists($key) { return isset($_SESSION[$key]); } /** * @param string $key */ public function remove($key) { if (isset($_SESSION[$key])) { unset($_SESSION[$key]); } } public function clear() { $this->invoke('session_unset'); $this->regenerateId(); $this->invoke('session_start', [], true); $_SESSION = []; } public function close() { $this->invoke('session_write_close'); parent::close(); } /** * Wrapper around session_regenerate_id * * @param bool $deleteOldSession Whether to delete the old associated session file or not. * @return void */ public function regenerateId($deleteOldSession = true) { try { @session_regenerate_id($deleteOldSession); } catch (\Error $e) { $this->trapError($e->getCode(), $e->getMessage()); } } /** * Wrapper around session_id * * @return string * @throws SessionNotAvailableException * @since 9.1.0 */ public function getId() { $id = $this->invoke('session_id', [], true); if ($id === '') { throw new SessionNotAvailableException(); } return $id; } /** * @throws \Exception */ public function reopen() { throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.'); } /** * @param int $errorNumber * @param string $errorString * @throws \ErrorException */ public function trapError($errorNumber, $errorString) { throw new \ErrorException($errorString); } /** * @throws \Exception */ private function validateSession() { if ($this->sessionClosed) { throw new SessionNotAvailableException('Session has been closed - no further changes to the session are allowed'); } } /** * @param string $functionName the full session_* function name * @param array $parameters * @param bool $silence whether to suppress warnings * @throws \ErrorException via trapError * @return mixed */ private function invoke($functionName, array $parameters = [], $silence = false) { try { if($silence) { return @call_user_func_array($functionName, $parameters); } else { return call_user_func_array($functionName, $parameters); } } catch(\Error $e) { $this->trapError($e->getCode(), $e->getMessage()); } } } private/Session/CryptoSessionData.php 0000604 00000011335 15247130453 0013773 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Session; use OCP\ISession; use OCP\Security\ICrypto; use OCP\Session\Exceptions\SessionNotAvailableException; /** * Class CryptoSessionData * * @package OC\Session */ class CryptoSessionData implements \ArrayAccess, ISession { /** @var ISession */ protected $session; /** @var \OCP\Security\ICrypto */ protected $crypto; /** @var string */ protected $passphrase; /** @var array */ protected $sessionValues; /** @var bool */ protected $isModified = false; CONST encryptedSessionName = 'encrypted_session_data'; /** * @param ISession $session * @param ICrypto $crypto * @param string $passphrase */ public function __construct(ISession $session, ICrypto $crypto, $passphrase) { $this->crypto = $crypto; $this->session = $session; $this->passphrase = $passphrase; $this->initializeSession(); } /** * Close session if class gets destructed */ public function __destruct() { try { $this->close(); } catch (SessionNotAvailableException $e){ // This exception can occur if session is already closed // So it is safe to ignore it and let the garbage collector to proceed } } protected function initializeSession() { $encryptedSessionData = $this->session->get(self::encryptedSessionName); try { $this->sessionValues = json_decode( $this->crypto->decrypt($encryptedSessionData, $this->passphrase), true ); } catch (\Exception $e) { $this->sessionValues = []; } } /** * Set a value in the session * * @param string $key * @param mixed $value */ public function set($key, $value) { $this->sessionValues[$key] = $value; $this->isModified = true; } /** * Get a value from the session * * @param string $key * @return string|null Either the value or null */ public function get($key) { if(isset($this->sessionValues[$key])) { return $this->sessionValues[$key]; } return null; } /** * Check if a named key exists in the session * * @param string $key * @return bool */ public function exists($key) { return isset($this->sessionValues[$key]); } /** * Remove a $key/$value pair from the session * * @param string $key */ public function remove($key) { $this->isModified = true; unset($this->sessionValues[$key]); $this->session->remove(self::encryptedSessionName); } /** * Reset and recreate the session */ public function clear() { $requesttoken = $this->get('requesttoken'); $this->sessionValues = []; if ($requesttoken !== null) { $this->set('requesttoken', $requesttoken); } $this->isModified = true; $this->session->clear(); } /** * Wrapper around session_regenerate_id * * @param bool $deleteOldSession Whether to delete the old associated session file or not. * @return void */ public function regenerateId($deleteOldSession = true) { $this->session->regenerateId($deleteOldSession); } /** * Wrapper around session_id * * @return string * @throws SessionNotAvailableException * @since 9.1.0 */ public function getId() { return $this->session->getId(); } /** * Close the session and release the lock, also writes all changed data in batch */ public function close() { if($this->isModified) { $encryptedValue = $this->crypto->encrypt(json_encode($this->sessionValues), $this->passphrase); $this->session->set(self::encryptedSessionName, $encryptedValue); $this->isModified = false; } $this->session->close(); } /** * @param mixed $offset * @return bool */ public function offsetExists($offset) { return $this->exists($offset); } /** * @param mixed $offset * @return mixed */ public function offsetGet($offset) { return $this->get($offset); } /** * @param mixed $offset * @param mixed $value */ public function offsetSet($offset, $value) { $this->set($offset, $value); } /** * @param mixed $offset */ public function offsetUnset($offset) { $this->remove($offset); } } private/Session/Memory.php 0000604 00000005472 15247130453 0011632 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Session; use Exception; use OCP\Session\Exceptions\SessionNotAvailableException; /** * Class Internal * * store session data in an in-memory array, not persistent * * @package OC\Session */ class Memory extends Session { protected $data; public function __construct($name) { //no need to use $name since all data is already scoped to this instance $this->data = array(); } /** * @param string $key * @param integer $value */ public function set($key, $value) { $this->validateSession(); $this->data[$key] = $value; } /** * @param string $key * @return mixed */ public function get($key) { if (!$this->exists($key)) { return null; } return $this->data[$key]; } /** * @param string $key * @return bool */ public function exists($key) { return isset($this->data[$key]); } /** * @param string $key */ public function remove($key) { $this->validateSession(); unset($this->data[$key]); } public function clear() { $this->data = array(); } /** * Stub since the session ID does not need to get regenerated for the cache * * @param bool $deleteOldSession */ public function regenerateId($deleteOldSession = true) {} /** * Wrapper around session_id * * @return string * @throws SessionNotAvailableException * @since 9.1.0 */ public function getId() { throw new SessionNotAvailableException('Memory session does not have an ID'); } /** * Helper function for PHPUnit execution - don't use in non-test code */ public function reopen() { $this->sessionClosed = false; } /** * In case the session has already been locked an exception will be thrown * * @throws Exception */ private function validateSession() { if ($this->sessionClosed) { throw new Exception('Session has been closed - no further changes to the session are allowed'); } } } private/Session/Session.php 0000604 00000003345 15247130453 0012002 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Session; use OCP\ISession; abstract class Session implements \ArrayAccess, ISession { /** * @var bool */ protected $sessionClosed = false; /** * $name serves as a namespace for the session keys * * @param string $name */ abstract public function __construct($name); /** * @param mixed $offset * @return bool */ public function offsetExists($offset) { return $this->exists($offset); } /** * @param mixed $offset * @return mixed */ public function offsetGet($offset) { return $this->get($offset); } /** * @param mixed $offset * @param mixed $value */ public function offsetSet($offset, $value) { $this->set($offset, $value); } /** * @param mixed $offset */ public function offsetUnset($offset) { $this->remove($offset); } /** * Close the session and release the lock */ public function close() { $this->sessionClosed = true; } } private/Session/CryptoWrapper.php 0000604 00000005765 15247130453 0013210 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Phil Davis <phil.davis@inf.org> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Session; use OCP\IConfig; use OCP\IRequest; use OCP\ISession; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; /** * Class CryptoWrapper provides some rough basic level of additional security by * storing the session data in an encrypted form. * * The content of the session is encrypted using another cookie sent by the browser. * One should note that an adversary with access to the source code or the system * memory is still able to read the original session ID from the users' request. * This thus can not be considered a strong security measure one should consider * it as an additional small security obfuscation layer to comply with compliance * guidelines. * * TODO: Remove this in a future release with an approach such as * https://github.com/owncloud/core/pull/17866 * * @package OC\Session */ class CryptoWrapper { const COOKIE_NAME = 'oc_sessionPassphrase'; /** @var ISession */ protected $session; /** @var \OCP\Security\ICrypto */ protected $crypto; /** @var ISecureRandom */ protected $random; /** * @param IConfig $config * @param ICrypto $crypto * @param ISecureRandom $random * @param IRequest $request */ public function __construct(IConfig $config, ICrypto $crypto, ISecureRandom $random, IRequest $request) { $this->crypto = $crypto; $this->config = $config; $this->random = $random; if (!is_null($request->getCookie(self::COOKIE_NAME))) { $this->passphrase = $request->getCookie(self::COOKIE_NAME); } else { $this->passphrase = $this->random->generate(128); $secureCookie = $request->getServerProtocol() === 'https'; // FIXME: Required for CI if (!defined('PHPUNIT_RUN')) { $webRoot = \OC::$WEBROOT; if($webRoot === '') { $webRoot = '/'; } setcookie(self::COOKIE_NAME, $this->passphrase, 0, $webRoot, '', $secureCookie, true); } } } /** * @param ISession $session * @return ISession */ public function wrapSession(ISession $session) { if (!($session instanceof CryptoSessionData)) { return new CryptoSessionData($session, $this->crypto, $this->passphrase); } return $session; } } private/ForbiddenException.php 0000604 00000001731 15247130453 0012504 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; /** * Exception thrown whenever access to a resource has * been forbidden or whenever a user isn't authenticated. */ class ForbiddenException extends \Exception { } private/Memcache/Cache.php 0000604 00000004130 15247130453 0011432 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; abstract class Cache implements \ArrayAccess, \OCP\ICache { /** * @var string $prefix */ protected $prefix; /** * @param string $prefix */ public function __construct($prefix = '') { $this->prefix = $prefix; } /** * @return string Prefix used for caching purposes */ public function getPrefix() { return $this->prefix; } /** * @param string $key * @return mixed */ abstract public function get($key); /** * @param string $key * @param mixed $value * @param int $ttl * @return mixed */ abstract public function set($key, $value, $ttl = 0); /** * @param string $key * @return mixed */ abstract public function hasKey($key); /** * @param string $key * @return mixed */ abstract public function remove($key); /** * @param string $prefix * @return mixed */ abstract public function clear($prefix = ''); //implement the ArrayAccess interface public function offsetExists($offset) { return $this->hasKey($offset); } public function offsetSet($offset, $value) { $this->set($offset, $value); } public function offsetGet($offset) { return $this->get($offset); } public function offsetUnset($offset) { $this->remove($offset); } } private/Memcache/XCache.php 0000604 00000007034 15247130453 0011570 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Clark Tomlinson <fallen013@gmail.com> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; use OCP\IMemcache; /** * See http://xcache.lighttpd.net/wiki/XcacheApi for provided constants and * functions etc. */ class XCache extends Cache implements IMemcache { use CASTrait; use CADTrait; /** * entries in XCache gets namespaced to prevent collisions between ownCloud instances and users */ protected function getNameSpace() { return $this->prefix; } public function get($key) { return xcache_get($this->getNamespace() . $key); } public function set($key, $value, $ttl = 0) { if ($ttl > 0) { return xcache_set($this->getNamespace() . $key, $value, $ttl); } else { return xcache_set($this->getNamespace() . $key, $value); } } public function hasKey($key) { return xcache_isset($this->getNamespace() . $key); } public function remove($key) { return xcache_unset($this->getNamespace() . $key); } public function clear($prefix = '') { if (function_exists('xcache_unset_by_prefix')) { return xcache_unset_by_prefix($this->getNamespace() . $prefix); } else { // Since we can not clear by prefix, we just clear the whole cache. xcache_clear_cache(\XC_TYPE_VAR, 0); } return true; } /** * Set a value in the cache if it's not already stored * * @param string $key * @param mixed $value * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 * @return bool */ public function add($key, $value, $ttl = 0) { if ($this->hasKey($key)) { return false; } else { return $this->set($key, $value, $ttl); } } /** * Increase a stored number * * @param string $key * @param int $step * @return int | bool */ public function inc($key, $step = 1) { return xcache_inc($this->getPrefix() . $key, $step); } /** * Decrease a stored number * * @param string $key * @param int $step * @return int | bool */ public function dec($key, $step = 1) { return xcache_dec($this->getPrefix() . $key, $step); } static public function isAvailable() { if (!extension_loaded('xcache')) { return false; } if (\OC::$CLI && !getenv('XCACHE_TEST')) { return false; } if (!function_exists('xcache_unset_by_prefix') && \OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) { // We do not want to use XCache if we can not clear it without // using the administration function xcache_clear_cache() // AND administration functions are password-protected. return false; } $var_size = \OC::$server->getIniWrapper()->getBytes('xcache.var_size'); if (!$var_size) { return false; } return true; } } private/Memcache/ArrayCache.php 0000604 00000006313 15247130453 0012436 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; use OCP\IMemcache; class ArrayCache extends Cache implements IMemcache { /** @var array Array with the cached data */ protected $cachedData = array(); use CADTrait; /** * {@inheritDoc} */ public function get($key) { if ($this->hasKey($key)) { return $this->cachedData[$key]; } return null; } /** * {@inheritDoc} */ public function set($key, $value, $ttl = 0) { $this->cachedData[$key] = $value; return true; } /** * {@inheritDoc} */ public function hasKey($key) { return isset($this->cachedData[$key]); } /** * {@inheritDoc} */ public function remove($key) { unset($this->cachedData[$key]); return true; } /** * {@inheritDoc} */ public function clear($prefix = '') { if ($prefix === '') { $this->cachedData = []; return true; } foreach ($this->cachedData as $key => $value) { if (strpos($key, $prefix) === 0) { $this->remove($key); } } return true; } /** * Set a value in the cache if it's not already stored * * @param string $key * @param mixed $value * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 * @return bool */ public function add($key, $value, $ttl = 0) { // since this cache is not shared race conditions aren't an issue if ($this->hasKey($key)) { return false; } else { return $this->set($key, $value, $ttl); } } /** * Increase a stored number * * @param string $key * @param int $step * @return int | bool */ public function inc($key, $step = 1) { $oldValue = $this->get($key); if (is_int($oldValue)) { $this->set($key, $oldValue + $step); return $oldValue + $step; } else { $success = $this->add($key, $step); return ($success) ? $step : false; } } /** * Decrease a stored number * * @param string $key * @param int $step * @return int | bool */ public function dec($key, $step = 1) { $oldValue = $this->get($key); if (is_int($oldValue)) { $this->set($key, $oldValue - $step); return $oldValue - $step; } else { return false; } } /** * Compare and set * * @param string $key * @param mixed $old * @param mixed $new * @return bool */ public function cas($key, $old, $new) { if ($this->get($key) === $old) { return $this->set($key, $new); } else { return false; } } /** * {@inheritDoc} */ static public function isAvailable() { return true; } } private/Memcache/Redis.php 0000604 00000010503 15247130453 0011476 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Stefan Weil <sw@weilnetz.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; use OCP\IMemcacheTTL; class Redis extends Cache implements IMemcacheTTL { /** * @var \Redis $cache */ private static $cache = null; public function __construct($prefix = '') { parent::__construct($prefix); if (is_null(self::$cache)) { self::$cache = \OC::$server->getGetRedisFactory()->getInstance(); } } /** * entries in redis get namespaced to prevent collisions between ownCloud instances and users */ protected function getNameSpace() { return $this->prefix; } public function get($key) { $result = self::$cache->get($this->getNameSpace() . $key); if ($result === false && !self::$cache->exists($this->getNameSpace() . $key)) { return null; } else { return json_decode($result, true); } } public function set($key, $value, $ttl = 0) { if ($ttl > 0) { return self::$cache->setex($this->getNameSpace() . $key, $ttl, json_encode($value)); } else { return self::$cache->set($this->getNameSpace() . $key, json_encode($value)); } } public function hasKey($key) { return self::$cache->exists($this->getNameSpace() . $key); } public function remove($key) { if (self::$cache->del($this->getNameSpace() . $key)) { return true; } else { return false; } } public function clear($prefix = '') { $prefix = $this->getNameSpace() . $prefix . '*'; $keys = self::$cache->keys($prefix); $deleted = self::$cache->del($keys); return count($keys) === $deleted; } /** * Set a value in the cache if it's not already stored * * @param string $key * @param mixed $value * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 * @return bool */ public function add($key, $value, $ttl = 0) { // don't encode ints for inc/dec if (!is_int($value)) { $value = json_encode($value); } return self::$cache->setnx($this->getPrefix() . $key, $value); } /** * Increase a stored number * * @param string $key * @param int $step * @return int | bool */ public function inc($key, $step = 1) { return self::$cache->incrBy($this->getNameSpace() . $key, $step); } /** * Decrease a stored number * * @param string $key * @param int $step * @return int | bool */ public function dec($key, $step = 1) { if (!$this->hasKey($key)) { return false; } return self::$cache->decrBy($this->getNameSpace() . $key, $step); } /** * Compare and set * * @param string $key * @param mixed $old * @param mixed $new * @return bool */ public function cas($key, $old, $new) { if (!is_int($new)) { $new = json_encode($new); } self::$cache->watch($this->getNameSpace() . $key); if ($this->get($key) === $old) { $result = self::$cache->multi() ->set($this->getNameSpace() . $key, $new) ->exec(); return ($result === false) ? false : true; } self::$cache->unwatch(); return false; } /** * Compare and delete * * @param string $key * @param mixed $old * @return bool */ public function cad($key, $old) { self::$cache->watch($this->getNameSpace() . $key); if ($this->get($key) === $old) { $result = self::$cache->multi() ->del($this->getNameSpace() . $key) ->exec(); return ($result === false) ? false : true; } self::$cache->unwatch(); return false; } public function setTTL($key, $ttl) { self::$cache->expire($this->getNameSpace() . $key, $ttl); } static public function isAvailable() { return \OC::$server->getGetRedisFactory()->isAvailable(); } } private/Memcache/APCu.php 0000604 00000010361 15247130453 0011222 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Clark Tomlinson <fallen013@gmail.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; use OCP\IMemcache; class APCu extends Cache implements IMemcache { use CASTrait { cas as casEmulated; } use CADTrait; public function get($key) { $result = apcu_fetch($this->getPrefix() . $key, $success); if (!$success) { return null; } return $result; } public function set($key, $value, $ttl = 0) { return apcu_store($this->getPrefix() . $key, $value, $ttl); } public function hasKey($key) { return apcu_exists($this->getPrefix() . $key); } public function remove($key) { return apcu_delete($this->getPrefix() . $key); } public function clear($prefix = '') { $ns = $this->getPrefix() . $prefix; $ns = preg_quote($ns, '/'); if(class_exists('\APCIterator')) { $iter = new \APCIterator('user', '/^' . $ns . '/', APC_ITER_KEY); } else { $iter = new \APCUIterator('/^' . $ns . '/', APC_ITER_KEY); } return apcu_delete($iter); } /** * Set a value in the cache if it's not already stored * * @param string $key * @param mixed $value * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 * @return bool */ public function add($key, $value, $ttl = 0) { return apcu_add($this->getPrefix() . $key, $value, $ttl); } /** * Increase a stored number * * @param string $key * @param int $step * @return int | bool */ public function inc($key, $step = 1) { $this->add($key, 0); /** * TODO - hack around a PHP 7 specific issue in APCu * * on PHP 7 the apcu_inc method on a non-existing object will increment * "0" and result in "1" as value - therefore we check for existence * first * * on PHP 5.6 this is not the case * * see https://github.com/krakjoe/apcu/issues/183#issuecomment-244038221 * for details */ return apcu_exists($this->getPrefix() . $key) ? apcu_inc($this->getPrefix() . $key, $step) : false; } /** * Decrease a stored number * * @param string $key * @param int $step * @return int | bool */ public function dec($key, $step = 1) { /** * TODO - hack around a PHP 7 specific issue in APCu * * on PHP 7 the apcu_dec method on a non-existing object will decrement * "0" and result in "-1" as value - therefore we check for existence * first * * on PHP 5.6 this is not the case * * see https://github.com/krakjoe/apcu/issues/183#issuecomment-244038221 * for details */ return apcu_exists($this->getPrefix() . $key) ? apcu_dec($this->getPrefix() . $key, $step) : false; } /** * Compare and set * * @param string $key * @param mixed $old * @param mixed $new * @return bool */ public function cas($key, $old, $new) { // apc only does cas for ints if (is_int($old) and is_int($new)) { return apcu_cas($this->getPrefix() . $key, $old, $new); } else { return $this->casEmulated($key, $old, $new); } } /** * @return bool */ static public function isAvailable() { if (!extension_loaded('apcu')) { return false; } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enabled')) { return false; } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enable_cli') && \OC::$CLI) { return false; } elseif ( version_compare(phpversion('apc'), '4.0.6') === -1 && version_compare(phpversion('apcu'), '5.1.0') === -1 ) { return false; } else { return true; } } } private/Memcache/Factory.php 0000604 00000012212 15247130453 0012036 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Markus Goetz <markus@woboq.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; use \OCP\ICacheFactory; use \OCP\ILogger; class Factory implements ICacheFactory { const NULL_CACHE = '\\OC\\Memcache\\NullCache'; /** * @var string $globalPrefix */ private $globalPrefix; /** * @var ILogger $logger */ private $logger; /** * @var string $localCacheClass */ private $localCacheClass; /** * @var string $distributedCacheClass */ private $distributedCacheClass; /** * @var string $lockingCacheClass */ private $lockingCacheClass; /** * @param string $globalPrefix * @param ILogger $logger * @param string|null $localCacheClass * @param string|null $distributedCacheClass * @param string|null $lockingCacheClass */ public function __construct($globalPrefix, ILogger $logger, $localCacheClass = null, $distributedCacheClass = null, $lockingCacheClass = null) { $this->logger = $logger; $this->globalPrefix = $globalPrefix; if (!$localCacheClass) { $localCacheClass = self::NULL_CACHE; } if (!$distributedCacheClass) { $distributedCacheClass = $localCacheClass; } $missingCacheMessage = 'Memcache {class} not available for {use} cache'; $missingCacheHint = 'Is the matching PHP module installed and enabled?'; if (!class_exists($localCacheClass) || !$localCacheClass::isAvailable()) { if (\OC::$CLI && !defined('PHPUNIT_RUN')) { // CLI should not hard-fail on broken memcache $this->logger->info($missingCacheMessage, [ 'class' => $localCacheClass, 'use' => 'local', 'app' => 'cli' ]); $localCacheClass = self::NULL_CACHE; } else { throw new \OC\HintException(strtr($missingCacheMessage, [ '{class}' => $localCacheClass, '{use}' => 'local' ]), $missingCacheHint); } } if (!class_exists($distributedCacheClass) || !$distributedCacheClass::isAvailable()) { if (\OC::$CLI && !defined('PHPUNIT_RUN')) { // CLI should not hard-fail on broken memcache $this->logger->info($missingCacheMessage, [ 'class' => $distributedCacheClass, 'use' => 'distributed', 'app' => 'cli' ]); $distributedCacheClass = self::NULL_CACHE; } else { throw new \OC\HintException(strtr($missingCacheMessage, [ '{class}' => $distributedCacheClass, '{use}' => 'distributed' ]), $missingCacheHint); } } if (!($lockingCacheClass && class_exists($distributedCacheClass) && $lockingCacheClass::isAvailable())) { // don't fallback since the fallback might not be suitable for storing lock $lockingCacheClass = self::NULL_CACHE; } $this->localCacheClass = $localCacheClass; $this->distributedCacheClass = $distributedCacheClass; $this->lockingCacheClass = $lockingCacheClass; } /** * create a cache instance for storing locks * * @param string $prefix * @return \OCP\IMemcache */ public function createLocking($prefix = '') { return new $this->lockingCacheClass($this->globalPrefix . '/' . $prefix); } /** * create a distributed cache instance * * @param string $prefix * @return \OC\Memcache\Cache */ public function createDistributed($prefix = '') { return new $this->distributedCacheClass($this->globalPrefix . '/' . $prefix); } /** * create a local cache instance * * @param string $prefix * @return \OC\Memcache\Cache */ public function createLocal($prefix = '') { return new $this->localCacheClass($this->globalPrefix . '/' . $prefix); } /** * @see \OC\Memcache\Factory::createDistributed() * @param string $prefix * @return \OC\Memcache\Cache */ public function create($prefix = '') { return $this->createDistributed($prefix); } /** * check memcache availability * * @return bool */ public function isAvailable() { return ($this->distributedCacheClass !== self::NULL_CACHE); } /** * @see \OC\Memcache\Factory::createLocal() * @param string $prefix * @return Cache */ public function createLowLatency($prefix = '') { return $this->createLocal($prefix); } /** * check local memcache availability * * @return bool */ public function isAvailableLowLatency() { return ($this->localCacheClass !== self::NULL_CACHE); } } private/Memcache/CASTrait.php 0000604 00000002651 15247130453 0012047 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; trait CASTrait { abstract public function get($key); abstract public function set($key, $value, $ttl = 0); abstract public function remove($key); abstract public function add($key, $value, $ttl = 0); /** * Compare and set * * @param string $key * @param mixed $old * @param mixed $new * @return bool */ public function cas($key, $old, $new) { //no native cas, emulate with locking if ($this->add($key . '_lock', true)) { if ($this->get($key) === $old) { $this->set($key, $new); $this->remove($key . '_lock'); return true; } else { $this->remove($key . '_lock'); return false; } } else { return false; } } } private/Memcache/Memcached.php 0000604 00000013605 15247130453 0012304 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; use OC\HintException; use OCP\IMemcache; class Memcached extends Cache implements IMemcache { use CASTrait; /** * @var \Memcached $cache */ private static $cache = null; use CADTrait; public function __construct($prefix = '') { parent::__construct($prefix); if (is_null(self::$cache)) { self::$cache = new \Memcached(); $defaultOptions = [ \Memcached::OPT_CONNECT_TIMEOUT => 50, \Memcached::OPT_RETRY_TIMEOUT => 50, \Memcached::OPT_SEND_TIMEOUT => 50, \Memcached::OPT_RECV_TIMEOUT => 50, \Memcached::OPT_POLL_TIMEOUT => 50, // Enable compression \Memcached::OPT_COMPRESSION => true, // Turn on consistent hashing \Memcached::OPT_LIBKETAMA_COMPATIBLE => true, // Enable Binary Protocol //\Memcached::OPT_BINARY_PROTOCOL => true, ]; // by default enable igbinary serializer if available if (\Memcached::HAVE_IGBINARY) { $defaultOptions[\Memcached::OPT_SERIALIZER] = \Memcached::SERIALIZER_IGBINARY; } $options = \OC::$server->getConfig()->getSystemValue('memcached_options', []); if (is_array($options)) { $options = $options + $defaultOptions; self::$cache->setOptions($options); } else { throw new HintException("Expected 'memcached_options' config to be an array, got $options"); } $servers = \OC::$server->getSystemConfig()->getValue('memcached_servers'); if (!$servers) { $server = \OC::$server->getSystemConfig()->getValue('memcached_server'); if ($server) { $servers = [$server]; } else { $servers = [['localhost', 11211]]; } } self::$cache->addServers($servers); } } /** * entries in XCache gets namespaced to prevent collisions between owncloud instances and users */ protected function getNameSpace() { return $this->prefix; } public function get($key) { $result = self::$cache->get($this->getNamespace() . $key); if ($result === false and self::$cache->getResultCode() == \Memcached::RES_NOTFOUND) { return null; } else { return $result; } } public function set($key, $value, $ttl = 0) { if ($ttl > 0) { $result = self::$cache->set($this->getNamespace() . $key, $value, $ttl); } else { $result = self::$cache->set($this->getNamespace() . $key, $value); } if ($result !== true) { $this->verifyReturnCode(); } return $result; } public function hasKey($key) { self::$cache->get($this->getNamespace() . $key); return self::$cache->getResultCode() === \Memcached::RES_SUCCESS; } public function remove($key) { $result= self::$cache->delete($this->getNamespace() . $key); if (self::$cache->getResultCode() !== \Memcached::RES_NOTFOUND) { $this->verifyReturnCode(); } return $result; } public function clear($prefix = '') { $prefix = $this->getNamespace() . $prefix; $allKeys = self::$cache->getAllKeys(); if ($allKeys === false) { // newer Memcached doesn't like getAllKeys(), flush everything self::$cache->flush(); return true; } $keys = array(); $prefixLength = strlen($prefix); foreach ($allKeys as $key) { if (substr($key, 0, $prefixLength) === $prefix) { $keys[] = $key; } } if (method_exists(self::$cache, 'deleteMulti')) { self::$cache->deleteMulti($keys); } else { foreach ($keys as $key) { self::$cache->delete($key); } } return true; } /** * Set a value in the cache if it's not already stored * * @param string $key * @param mixed $value * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 * @return bool * @throws \Exception */ public function add($key, $value, $ttl = 0) { $result = self::$cache->add($this->getPrefix() . $key, $value, $ttl); if (self::$cache->getResultCode() !== \Memcached::RES_NOTSTORED) { $this->verifyReturnCode(); } return $result; } /** * Increase a stored number * * @param string $key * @param int $step * @return int | bool */ public function inc($key, $step = 1) { $this->add($key, 0); $result = self::$cache->increment($this->getPrefix() . $key, $step); if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) { return false; } return $result; } /** * Decrease a stored number * * @param string $key * @param int $step * @return int | bool */ public function dec($key, $step = 1) { $result = self::$cache->decrement($this->getPrefix() . $key, $step); if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) { return false; } return $result; } static public function isAvailable() { return extension_loaded('memcached'); } /** * @throws \Exception */ private function verifyReturnCode() { $code = self::$cache->getResultCode(); if ($code === \Memcached::RES_SUCCESS) { return; } $message = self::$cache->getResultMessage(); throw new \Exception("Error $code interacting with memcached : $message"); } } private/Memcache/NullCache.php 0000604 00000003200 15247130453 0012262 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; class NullCache extends Cache implements \OCP\IMemcache { public function get($key) { return null; } public function set($key, $value, $ttl = 0) { return true; } public function hasKey($key) { return false; } public function remove($key) { return true; } public function add($key, $value, $ttl = 0) { return true; } public function inc($key, $step = 1) { return true; } public function dec($key, $step = 1) { return true; } public function cas($key, $old, $new) { return true; } public function cad($key, $old) { return true; } public function clear($prefix = '') { return true; } static public function isAvailable() { return true; } } private/Memcache/CADTrait.php 0000604 00000002525 15247130453 0012030 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Memcache; trait CADTrait { abstract public function get($key); abstract public function remove($key); abstract public function add($key, $value, $ttl = 0); /** * Compare and delete * * @param string $key * @param mixed $old * @return bool */ public function cad($key, $old) { //no native cas, emulate with locking if ($this->add($key . '_lock', true)) { if ($this->get($key) === $old) { $this->remove($key); $this->remove($key . '_lock'); return true; } else { $this->remove($key . '_lock'); return false; } } else { return false; } } } private/Mail/Mailer.php 0000604 00000016744 15247130453 0011036 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Mail; use OCP\Defaults; use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; use OCP\Mail\IEMailTemplate; use OCP\Mail\IMailer; use OCP\ILogger; /** * Class Mailer provides some basic functions to create a mail message that can be used in combination with * \OC\Mail\Message. * * Example usage: * * $mailer = \OC::$server->getMailer(); * $message = $mailer->createMessage(); * $message->setSubject('Your Subject'); * $message->setFrom(array('cloud@domain.org' => 'ownCloud Notifier'); * $message->setTo(array('recipient@domain.org' => 'Recipient'); * $message->setBody('The message text'); * $mailer->send($message); * * This message can then be passed to send() of \OC\Mail\Mailer * * @package OC\Mail */ class Mailer implements IMailer { /** @var \Swift_SmtpTransport|\Swift_SendmailTransport|\Swift_MailTransport Cached transport */ private $instance = null; /** @var IConfig */ private $config; /** @var ILogger */ private $logger; /** @var Defaults */ private $defaults; /** @var IURLGenerator */ private $urlGenerator; /** @var IL10N */ private $l10n; /** * @param IConfig $config * @param ILogger $logger * @param Defaults $defaults * @param IURLGenerator $urlGenerator * @param IL10N $l10n */ public function __construct(IConfig $config, ILogger $logger, Defaults $defaults, IURLGenerator $urlGenerator, IL10N $l10n) { $this->config = $config; $this->logger = $logger; $this->defaults = $defaults; $this->urlGenerator = $urlGenerator; $this->l10n = $l10n; } /** * Creates a new message object that can be passed to send() * * @return Message */ public function createMessage() { return new Message(new \Swift_Message()); } /** * Creates a new email template object * * @param string $emailId * @param array $data * @return IEMailTemplate * @since 12.0.0 */ public function createEMailTemplate($emailId, array $data = []) { $class = $this->config->getSystemValue('mail_template_class', ''); if ($class !== '' && class_exists($class) && is_a($class, EMailTemplate::class, true)) { return new $class( $this->defaults, $this->urlGenerator, $this->l10n, $emailId, $data ); } return new EMailTemplate( $this->defaults, $this->urlGenerator, $this->l10n, $emailId, $data ); } /** * Send the specified message. Also sets the from address to the value defined in config.php * if no-one has been passed. * * @param Message $message Message to send * @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and * therefore should be considered * @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address * has been supplied.) */ public function send(Message $message) { $debugMode = $this->config->getSystemValue('mail_smtpdebug', false); if (sizeof($message->getFrom()) === 0) { $message->setFrom([\OCP\Util::getDefaultEmailAddress($this->defaults->getName()) => $this->defaults->getName()]); } $failedRecipients = []; $mailer = $this->getInstance(); // Enable logger if debug mode is enabled if($debugMode) { $mailLogger = new \Swift_Plugins_Loggers_ArrayLogger(); $mailer->registerPlugin(new \Swift_Plugins_LoggerPlugin($mailLogger)); } $mailer->send($message->getSwiftMessage(), $failedRecipients); // Debugging logging $logMessage = sprintf('Sent mail to "%s" with subject "%s"', print_r($message->getTo(), true), $message->getSubject()); $this->logger->debug($logMessage, ['app' => 'core']); if($debugMode && isset($mailLogger)) { $this->logger->debug($mailLogger->dump(), ['app' => 'core']); } return $failedRecipients; } /** * Checks if an e-mail address is valid * * @param string $email Email address to be validated * @return bool True if the mail address is valid, false otherwise */ public function validateMailAddress($email) { return \Swift_Validate::email($this->convertEmail($email)); } /** * SwiftMailer does currently not work with IDN domains, this function therefore converts the domains * * FIXME: Remove this once SwiftMailer supports IDN * * @param string $email * @return string Converted mail address if `idn_to_ascii` exists */ protected function convertEmail($email) { if (!function_exists('idn_to_ascii') || strpos($email, '@') === false) { return $email; } list($name, $domain) = explode('@', $email, 2); $domain = idn_to_ascii($domain); return $name.'@'.$domain; } /** * Returns whatever transport is configured within the config * * @return \Swift_SmtpTransport|\Swift_SendmailTransport|\Swift_MailTransport */ protected function getInstance() { if (!is_null($this->instance)) { return $this->instance; } switch ($this->config->getSystemValue('mail_smtpmode', 'php')) { case 'smtp': $this->instance = $this->getSMTPInstance(); break; case 'sendmail': // FIXME: Move into the return statement but requires proper testing // for SMTP and mail as well. Thus not really doable for a // minor release. $this->instance = \Swift_Mailer::newInstance($this->getSendMailInstance()); break; default: $this->instance = $this->getMailInstance(); break; } return $this->instance; } /** * Returns the SMTP transport * * @return \Swift_SmtpTransport */ protected function getSmtpInstance() { $transport = \Swift_SmtpTransport::newInstance(); $transport->setTimeout($this->config->getSystemValue('mail_smtptimeout', 10)); $transport->setHost($this->config->getSystemValue('mail_smtphost', '127.0.0.1')); $transport->setPort($this->config->getSystemValue('mail_smtpport', 25)); if ($this->config->getSystemValue('mail_smtpauth', false)) { $transport->setUsername($this->config->getSystemValue('mail_smtpname', '')); $transport->setPassword($this->config->getSystemValue('mail_smtppassword', '')); $transport->setAuthMode($this->config->getSystemValue('mail_smtpauthtype', 'LOGIN')); } $smtpSecurity = $this->config->getSystemValue('mail_smtpsecure', ''); if (!empty($smtpSecurity)) { $transport->setEncryption($smtpSecurity); } $transport->start(); return $transport; } /** * Returns the sendmail transport * * @return \Swift_SendmailTransport */ protected function getSendMailInstance() { switch ($this->config->getSystemValue('mail_smtpmode', 'php')) { case 'qmail': $binaryPath = '/var/qmail/bin/sendmail'; break; default: $binaryPath = '/usr/sbin/sendmail'; break; } return \Swift_SendmailTransport::newInstance($binaryPath . ' -bs'); } /** * Returns the mail transport * * @return \Swift_MailTransport */ protected function getMailInstance() { return \Swift_MailTransport::newInstance(); } } private/Mail/EMailTemplate.php 0000604 00000100403 15247130453 0012272 0 ustar 00 <?php /** * @copyright 2017, Morris Jobke <hey@morrisjobke.de> * @copyright 2017, Lukas Reschke <lukas@statuscode.ch> * * @author Morris Jobke <hey@morrisjobke.de> * @author Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OC\Mail; use OCP\Defaults; use OCP\IL10N; use OCP\IURLGenerator; use OCP\Mail\IEMailTemplate; /** * Class EMailTemplate * * addBodyText and addBodyButtonGroup automatically opens the body * addFooter, renderHtml, renderText automatically closes the body and the HTML if opened * * @package OC\Mail */ class EMailTemplate implements IEMailTemplate { /** @var Defaults */ protected $themingDefaults; /** @var IURLGenerator */ protected $urlGenerator; /** @var IL10N */ protected $l10n; /** @var string */ protected $emailId; /** @var array */ protected $data; /** @var string */ protected $htmlBody = ''; /** @var string */ protected $plainBody = ''; /** @var bool indicated if the footer is added */ protected $headerAdded = false; /** @var bool indicated if the body is already opened */ protected $bodyOpened = false; /** @var bool indicated if there is a list open in the body */ protected $bodyListOpened = false; /** @var bool indicated if the footer is added */ protected $footerAdded = false; protected $head = <<<EOF <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" style="-webkit-font-smoothing:antialiased;background:#f3f3f3!important"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width"> <title></title> <style type="text/css">@media only screen{html{min-height:100%;background:#F5F5F5}}@media only screen and (max-width:610px){table.body img{width:auto;height:auto}table.body center{min-width:0!important}table.body .container{width:95%!important}table.body .columns{height:auto!important;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:30px!important;padding-right:30px!important}th.small-12{display:inline-block!important;width:100%!important}table.menu{width:100%!important}table.menu td,table.menu th{width:auto!important;display:inline-block!important}table.menu.vertical td,table.menu.vertical th{display:block!important}table.menu[align=center]{width:auto!important}}</style> </head> <body style="-moz-box-sizing:border-box;-ms-text-size-adjust:100%;-webkit-box-sizing:border-box;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;Margin:0;background:#f3f3f3!important;box-sizing:border-box;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;min-width:100%;padding:0;text-align:left;width:100%!important"> <span class="preheader" style="color:#F5F5F5;display:none!important;font-size:1px;line-height:1px;max-height:0;max-width:0;mso-hide:all!important;opacity:0;overflow:hidden;visibility:hidden"> </span> <table class="body" style="-webkit-font-smoothing:antialiased;Margin:0;background:#f3f3f3!important;border-collapse:collapse;border-spacing:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;height:100%;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;width:100%"> <tr style="padding:0;text-align:left;vertical-align:top"> <td class="center" align="center" valign="top" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <center data-parsed="" style="min-width:580px;width:100%"> EOF; protected $tail = <<<EOF </center> </td> </tr> </table> <!-- prevent Gmail on iOS font size manipulation --> <div style="display:none;white-space:nowrap;font:15px courier;line-height:0"> </div> </body> </html> EOF; protected $header = <<<EOF <table align="center" class="wrapper header float-center" style="Margin:0 auto;background:#8a8a8a;background-color:%s;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%"> <tr style="padding:0;text-align:left;vertical-align:top"> <td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:20px;text-align:left;vertical-align:top;word-wrap:break-word"> <table align="center" class="container" style="Margin:0 auto;background:0 0;border-collapse:collapse;border-spacing:0;margin:0 auto;padding:0;text-align:inherit;vertical-align:top;width:580px"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <table class="row collapse" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <center data-parsed="" style="min-width:580px;width:100%%"> <img class="logo float-center" src="%s" alt="%s" align="center" style="-ms-interpolation-mode:bicubic;Margin:0 auto;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none" height="50"> </center> </tr> </tbody> </table> </td> </tr> </tbody> </table> </td> </tr> </table> <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td height="80px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:80px;font-weight:400;hyphens:auto;line-height:80px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> </td> </tr> </tbody> </table> EOF; protected $heading = <<<EOF <table align="center" class="container main-heading float-center" style="Margin:0 auto;background:0 0!important;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:580px"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <h1 class="text-center" style="Margin:0;Margin-bottom:10px;color:inherit;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:24px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;text-align:center;word-wrap:normal">%s</h1> </td> </tr> </tbody> </table> <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td height="40px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:40px;font-weight:400;hyphens:auto;line-height:40px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> </td> </tr> </tbody> </table> EOF; protected $bodyBegin = <<<EOF <table align="center" class="wrapper content float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%"> <tr style="padding:0;text-align:left;vertical-align:top"> <td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <table align="center" class="container has-shadow" style="Margin:0 auto;background:#fefefe;border-collapse:collapse;border-spacing:0;box-shadow:0 1px 2px 0 rgba(0,0,0,.2),0 1px 3px 0 rgba(0,0,0,.1);margin:0 auto;padding:0;text-align:inherit;vertical-align:top;width:580px"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td height="60px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:60px;font-weight:400;hyphens:auto;line-height:60px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> </td> </tr> </tbody> </table> EOF; protected $bodyText = <<<EOF <table class="row description" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:30px;padding-left:30px;padding-right:30px;text-align:left;width:550px"> <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> <p class="text-left" style="Margin:0;Margin-bottom:10px;color:#777;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;text-align:left">%s</p> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> </tr> </table> </th> </tr> </tbody> </table> EOF; protected $listBegin = <<<EOF <table class="row description" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:30px;padding-left:30px;padding-right:30px;text-align:left;width:550px"> <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> EOF; protected $listItem = <<<EOF <tr style="padding:0;text-align:left;vertical-align:top"> <td style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left;width:15px;"> <p class="text-left" style="Margin:0;Margin-bottom:10px;color:#777;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;padding-left:10px;text-align:left">%s</p> </td> <td style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> <p class="text-left" style="Margin:0;Margin-bottom:10px;color:#555;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;padding-left:10px;text-align:left">%s</p> </td> <td class="expander" style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></td> </tr> EOF; protected $listEnd = <<<EOF </table> </th> </tr> </tbody> </table> EOF; protected $buttonGroup = <<<EOF <table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td height="50px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:50px;font-weight:400;hyphens:auto;line-height:50px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> </td> </tr> </tbody> </table> <table align="center" class="row btn-group" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:30px;padding-left:30px;padding-right:30px;text-align:left;width:550px"> <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> <center data-parsed="" style="min-width:490px;width:100%%"> <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;max-height:40px;max-width:200px;padding:0;text-align:center;vertical-align:top;width:auto"> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:%s;border:0 solid %s;border-collapse:collapse!important;color:#fefefe;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <a href="%s" style="Margin:0;border:0 solid %s;border-radius:2px;color:#fefefe;display:inline-block;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;text-decoration:none">%s</a> </td> </tr> </table> </td> </tr> </table> <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;max-height:40px;max-width:200px;padding:0;text-align:center;vertical-align:top;width:auto"> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:#777;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <a href="%s" style="Margin:0;background-color:#fff;border:0 solid #777;border-radius:2px;color:#6C6C6C!important;display:inline-block;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;outline:1px solid #CBCBCB;padding:10px 25px 10px 25px;text-align:left;text-decoration:none">%s</a> </td> </tr> </table> </td> </tr> </table> </center> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> </tr> </table> </th> </tr> </tbody> </table> EOF; protected $button = <<<EOF <table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td height="50px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:50px;font-weight:400;hyphens:auto;line-height:50px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> </td> </tr> </tbody> </table> <table align="center" class="row btn-group" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:30px;padding-left:30px;padding-right:30px;text-align:left;width:550px"> <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> <center data-parsed="" style="min-width:490px;width:100%%"> <table class="button btn default primary float-center" style="Margin:0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0;max-height:40px;padding:0;text-align:center;vertical-align:top;width:auto"> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> <tr style="padding:0;text-align:left;vertical-align:top"> <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:%s;border:0 solid %s;border-collapse:collapse!important;color:#fefefe;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <a href="%s" style="Margin:0;border:0 solid %s;border-radius:2px;color:#fefefe;display:inline-block;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;text-decoration:none">%s</a> </td> </tr> </table> </td> </tr> </table> </center> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> </tr> </table> </th> </tr> </tbody> </table> EOF; protected $bodyEnd = <<<EOF </td> </tr> </tbody> </table> </td> </tr> </table> EOF; protected $footer = <<<EOF <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td height="60px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:60px;font-weight:400;hyphens:auto;line-height:60px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> </td> </tr> </tbody> </table> <table align="center" class="wrapper footer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%"> <tr style="padding:0;text-align:left;vertical-align:top"> <td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> <center data-parsed="" style="min-width:580px;width:100%%"> <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> <td height="15px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:15px;font-weight:400;hyphens:auto;line-height:15px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> </td> </tr> </tbody> </table> <p class="text-center float-center" align="center" style="Margin:0;Margin-bottom:10px;color:#C8C8C8;font-family:Lucida Grande,Geneva,Verdana,sans-serif;font-size:12px;font-weight:400;line-height:16px;margin:0;margin-bottom:10px;padding:0;text-align:center">%s</p> </center> </td> </tr> </table> EOF; /** * @param Defaults $themingDefaults * @param IURLGenerator $urlGenerator * @param IL10N $l10n * @param string $emailId * @param array $data */ public function __construct(Defaults $themingDefaults, IURLGenerator $urlGenerator, IL10N $l10n, $emailId, array $data) { $this->themingDefaults = $themingDefaults; $this->urlGenerator = $urlGenerator; $this->l10n = $l10n; $this->htmlBody .= $this->head; $this->emailId = $emailId; $this->data = $data; } /** * Adds a header to the email */ public function addHeader() { if ($this->headerAdded) { return; } $this->headerAdded = true; $logoUrl = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo(false)); $this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getColorPrimary(), $logoUrl, $this->themingDefaults->getName()]); } /** * Adds a heading to the email * * @param string $title * @param string $plainTitle|bool Title that is used in the plain text email * if empty the $title is used, if false none will be used */ public function addHeading($title, $plainTitle = '') { if ($this->footerAdded) { return; } if ($plainTitle === '') { $plainTitle = $title; } $this->htmlBody .= vsprintf($this->heading, [htmlspecialchars($title)]); if ($plainTitle !== false) { $this->plainBody .= $plainTitle . PHP_EOL . PHP_EOL; } } /** * Open the HTML body when it is not already */ protected function ensureBodyIsOpened() { if ($this->bodyOpened) { return; } $this->htmlBody .= $this->bodyBegin; $this->bodyOpened = true; } /** * Adds a paragraph to the body of the email * * @param string $text * @param string|bool $plainText Text that is used in the plain text email * if empty the $text is used, if false none will be used */ public function addBodyText($text, $plainText = '') { if ($this->footerAdded) { return; } if ($plainText === '') { $plainText = $text; } $this->ensureBodyIsOpened(); $this->htmlBody .= vsprintf($this->bodyText, [htmlspecialchars($text)]); if ($plainText !== false) { $this->plainBody .= $plainText . PHP_EOL . PHP_EOL; } } /** * Adds a list item to the body of the email * * @param string $text * @param string $metaInfo * @param string $icon Absolute path, must be 16*16 pixels * @param string $plainText Text that is used in the plain text email * if empty the $text is used, if false none will be used * @param string $plainMetaInfo Meta info that is used in the plain text email * if empty the $metaInfo is used, if false none will be used * @since 12.0.0 */ public function addBodyListItem($text, $metaInfo = '', $icon = '', $plainText = '', $plainMetaInfo = '') { $this->ensureBodyListOpened(); if ($plainText === '') { $plainText = $text; } if ($plainMetaInfo === '') { $plainMetaInfo = $metaInfo; } $htmlText = htmlspecialchars($text); if ($metaInfo) { $htmlText = '<em style="color:#777;">' . htmlspecialchars($metaInfo) . '</em><br>' . $htmlText; } if ($icon !== '') { $icon = '<img src="' . htmlspecialchars($icon) . '" alt="•">'; } else { $icon = '•'; } $this->htmlBody .= vsprintf($this->listItem, [$icon, $htmlText]); if ($plainText !== false) { $this->plainBody .= ' * ' . $plainText; if ($plainMetaInfo !== false) { $this->plainBody .= ' (' . $plainMetaInfo . ')'; } $this->plainBody .= PHP_EOL; } } protected function ensureBodyListOpened() { if ($this->bodyListOpened) { return; } $this->ensureBodyIsOpened(); $this->bodyListOpened = true; $this->htmlBody .= $this->listBegin; } protected function ensureBodyListClosed() { if (!$this->bodyListOpened) { return; } $this->bodyListOpened = false; $this->htmlBody .= $this->listEnd; } /** * Adds a button group of two buttons to the body of the email * * @param string $textLeft Text of left button * @param string $urlLeft URL of left button * @param string $textRight Text of right button * @param string $urlRight URL of right button * @param string $plainTextLeft Text of left button that is used in the plain text version - if unset the $textLeft is used * @param string $plainTextRight Text of right button that is used in the plain text version - if unset the $textRight is used */ public function addBodyButtonGroup($textLeft, $urlLeft, $textRight, $urlRight, $plainTextLeft = '', $plainTextRight = '') { if ($this->footerAdded) { return; } if ($plainTextLeft === '') { $plainTextLeft = $textLeft; } if ($plainTextRight === '') { $plainTextRight = $textRight; } $this->ensureBodyIsOpened(); $this->ensureBodyListClosed(); $color = $this->themingDefaults->getColorPrimary(); $this->htmlBody .= vsprintf($this->buttonGroup, [$color, $color, $urlLeft, $color, htmlspecialchars($textLeft), $urlRight, htmlspecialchars($textRight)]); $this->plainBody .= $plainTextLeft . ': ' . $urlLeft . PHP_EOL; $this->plainBody .= $plainTextRight . ': ' . $urlRight . PHP_EOL . PHP_EOL; } /** * Adds a button to the body of the email * * @param string $text Text of button * @param string $url URL of button * @param string $plainText Text of button in plain text version * if empty the $text is used, if false none will be used * * @since 12.0.0 */ public function addBodyButton($text, $url, $plainText = '') { if ($this->footerAdded) { return; } $this->ensureBodyIsOpened(); $this->ensureBodyListClosed(); if ($plainText === '') { $plainText = $text; } $color = $this->themingDefaults->getColorPrimary(); $this->htmlBody .= vsprintf($this->button, [$color, $color, $url, $color, htmlspecialchars($text)]); if ($plainText !== false) { $this->plainBody .= $plainText . ': '; } $this->plainBody .= $url . PHP_EOL; } /** * Close the HTML body when it is open */ protected function ensureBodyIsClosed() { if (!$this->bodyOpened) { return; } $this->ensureBodyListClosed(); $this->htmlBody .= $this->bodyEnd; $this->bodyOpened = false; } /** * Adds a logo and a text to the footer. <br> in the text will be replaced by new lines in the plain text email * * @param string $text If the text is empty the default "Name - Slogan<br>This is an automatically sent email" will be used */ public function addFooter($text = '') { if($text === '') { $text = $this->themingDefaults->getName() . ' - ' . $this->themingDefaults->getSlogan() . '<br>' . $this->l10n->t('This is an automatically sent email, please do not reply.'); } if ($this->footerAdded) { return; } $this->footerAdded = true; $this->ensureBodyIsClosed(); $this->htmlBody .= vsprintf($this->footer, [$text]); $this->htmlBody .= $this->tail; $this->plainBody .= PHP_EOL . '-- ' . PHP_EOL; $this->plainBody .= str_replace('<br>', PHP_EOL, $text); } /** * Returns the rendered HTML email as string * * @return string */ public function renderHtml() { if (!$this->footerAdded) { $this->footerAdded = true; $this->ensureBodyIsClosed(); $this->htmlBody .= $this->tail; } return $this->htmlBody; } /** * Returns the rendered plain text email as string * * @return string */ public function renderText() { if (!$this->footerAdded) { $this->footerAdded = true; $this->ensureBodyIsClosed(); $this->htmlBody .= $this->tail; } return $this->plainBody; } } private/Mail/Message.php 0000604 00000013310 15247130453 0011173 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Mail; use Swift_Message; /** * Class Message provides a wrapper around SwiftMail * * @package OC\Mail */ class Message { /** @var Swift_Message */ private $swiftMessage; /** * @param Swift_Message $swiftMessage */ function __construct(Swift_Message $swiftMessage) { $this->swiftMessage = $swiftMessage; } /** * SwiftMailer does currently not work with IDN domains, this function therefore converts the domains * FIXME: Remove this once SwiftMailer supports IDN * * @param array $addresses Array of mail addresses, key will get converted * @return array Converted addresses if `idn_to_ascii` exists */ protected function convertAddresses($addresses) { if (!function_exists('idn_to_ascii')) { return $addresses; } $convertedAddresses = array(); foreach($addresses as $email => $readableName) { if(!is_numeric($email)) { list($name, $domain) = explode('@', $email, 2); $domain = idn_to_ascii($domain); $convertedAddresses[$name.'@'.$domain] = $readableName; } else { list($name, $domain) = explode('@', $readableName, 2); $domain = idn_to_ascii($domain); $convertedAddresses[$email] = $name.'@'.$domain; } } return $convertedAddresses; } /** * Set the from address of this message. * * If no "From" address is used \OC\Mail\Mailer will use mail_from_address and mail_domain from config.php * * @param array $addresses Example: array('sender@domain.org', 'other@domain.org' => 'A name') * @return $this */ public function setFrom(array $addresses) { $addresses = $this->convertAddresses($addresses); $this->swiftMessage->setFrom($addresses); return $this; } /** * Get the from address of this message. * * @return array */ public function getFrom() { return $this->swiftMessage->getFrom(); } /** * Set the Reply-To address of this message * * @param array $addresses * @return $this */ public function setReplyTo(array $addresses) { $addresses = $this->convertAddresses($addresses); $this->swiftMessage->setReplyTo($addresses); return $this; } /** * Returns the Reply-To address of this message * * @return array */ public function getReplyTo() { return $this->swiftMessage->getReplyTo(); } /** * Set the to addresses of this message. * * @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name') * @return $this */ public function setTo(array $recipients) { $recipients = $this->convertAddresses($recipients); $this->swiftMessage->setTo($recipients); return $this; } /** * Get the to address of this message. * * @return array */ public function getTo() { return $this->swiftMessage->getTo(); } /** * Set the CC recipients of this message. * * @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name') * @return $this */ public function setCc(array $recipients) { $recipients = $this->convertAddresses($recipients); $this->swiftMessage->setCc($recipients); return $this; } /** * Get the cc address of this message. * * @return array */ public function getCc() { return $this->swiftMessage->getCc(); } /** * Set the BCC recipients of this message. * * @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name') * @return $this */ public function setBcc(array $recipients) { $recipients = $this->convertAddresses($recipients); $this->swiftMessage->setBcc($recipients); return $this; } /** * Get the Bcc address of this message. * * @return array */ public function getBcc() { return $this->swiftMessage->getBcc(); } /** * Set the subject of this message. * * @param $subject * @return $this */ public function setSubject($subject) { $this->swiftMessage->setSubject($subject); return $this; } /** * Get the from subject of this message. * * @return string */ public function getSubject() { return $this->swiftMessage->getSubject(); } /** * Set the plain-text body of this message. * * @param string $body * @return $this */ public function setPlainBody($body) { $this->swiftMessage->setBody($body); return $this; } /** * Get the plain body of this message. * * @return string */ public function getPlainBody() { return $this->swiftMessage->getBody(); } /** * Set the HTML body of this message. Consider also sending a plain-text body instead of only an HTML one. * * @param string $body * @return $this */ public function setHtmlBody($body) { $this->swiftMessage->addPart($body, 'text/html'); return $this; } /** * Get's the underlying SwiftMessage * @return Swift_Message */ public function getSwiftMessage() { return $this->swiftMessage; } /** * @param string $body * @param string $contentType * @return $this */ public function setBody($body, $contentType) { $this->swiftMessage->setBody($body, $contentType); return $this; } } private/Setup/PostgreSQL.php 0000604 00000013270 15247130453 0012035 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author eduardo <eduardo@vnexu.net> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Setup; use OC\DatabaseException; use OC\DB\QueryBuilder\Literal; use OCP\IDBConnection; class PostgreSQL extends AbstractDatabase { public $dbprettyname = 'PostgreSQL'; public function setupDatabase($username) { try { $connection = $this->connect([ 'dbname' => 'postgres' ]); //check for roles creation rights in postgresql $builder = $connection->getQueryBuilder(); $builder->automaticTablePrefix(false); $query = $builder ->select('rolname') ->from('pg_roles') ->where($builder->expr()->eq('rolcreaterole', new Literal('TRUE'))) ->andWhere($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser))); try { $result = $query->execute(); $canCreateRoles = $result->rowCount() > 0; } catch (DatabaseException $e) { $canCreateRoles = false; } if ($canCreateRoles) { //use the admin login data for the new database user //add prefix to the postgresql user name to prevent collisions $this->dbUser = 'oc_' . strtolower($username); //create a new password so we don't need to store the admin config in the config file $this->dbPassword = \OC::$server->getSecureRandom()->generate(30, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_DIGITS); $this->createDBUser($connection); } $this->config->setValues([ 'dbuser' => $this->dbUser, 'dbpassword' => $this->dbPassword, ]); //create the database $this->createDatabase($connection); $query = $connection->prepare("select count(*) FROM pg_class WHERE relname=? limit 1"); $query->execute([$this->tablePrefix . "users"]); $tablesSetup = $query->fetchColumn() > 0; // the connection to dbname=postgres is not needed anymore $connection->close(); } catch (\Exception $e) { $this->logger->logException($e); $this->logger->warning('Error trying to connect as "postgres", assuming database is setup and tables need to be created'); $tablesSetup = false; $this->config->setValues([ 'dbuser' => $this->dbUser, 'dbpassword' => $this->dbPassword, ]); } // connect to the ownCloud database (dbname=$this->dbname) and check if it needs to be filled $this->dbUser = $this->config->getValue('dbuser'); $this->dbPassword = $this->config->getValue('dbpassword'); $connection = $this->connect(); try { $connection->connect(); } catch (\Exception $e) { $this->logger->logException($e); throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'), $this->trans->t('You need to enter details of an existing account.')); } if (!$tablesSetup) { \OC_DB::createDbFromStructure($this->dbDefinitionFile); } } private function createDatabase(IDBConnection $connection) { if (!$this->databaseExists($connection)) { //The database does not exists... let's create it $query = $connection->prepare("CREATE DATABASE " . addslashes($this->dbName) . " OWNER " . addslashes($this->dbUser)); try { $query->execute(); } catch (DatabaseException $e) { $this->logger->error('Error while trying to create database'); $this->logger->logException($e); } } else { $query = $connection->prepare("REVOKE ALL PRIVILEGES ON DATABASE " . addslashes($this->dbName) . " FROM PUBLIC"); try { $query->execute(); } catch (DatabaseException $e) { $this->logger->error('Error while trying to restrict database permissions'); $this->logger->logException($e); } } } private function userExists(IDBConnection $connection) { $builder = $connection->getQueryBuilder(); $builder->automaticTablePrefix(false); $query = $builder->select('*') ->from('pg_roles') ->where($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser))); $result = $query->execute(); return $result->rowCount() > 0; } private function databaseExists(IDBConnection $connection) { $builder = $connection->getQueryBuilder(); $builder->automaticTablePrefix(false); $query = $builder->select('datname') ->from('pg_database') ->where($builder->expr()->eq('datname', $builder->createNamedParameter($this->dbName))); $result = $query->execute(); return $result->rowCount() > 0; } private function createDBUser(IDBConnection $connection) { $dbUser = $this->dbUser; try { $i = 1; while ($this->userExists($connection)) { $i++; $this->dbUser = $dbUser . $i; }; // create the user $query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'"); $query->execute(); } catch (DatabaseException $e) { $this->logger->error('Error while trying to create database user'); $this->logger->logException($e); } } } private/Setup/AbstractDatabase.php 0000604 00000010624 15247130453 0013222 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Manish Bisht <manish.bisht490@gmail.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Pulzer <t.pulzer@kniel.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Setup; use OC\DB\ConnectionFactory; use OC\SystemConfig; use OCP\IL10N; use OCP\ILogger; use OCP\Security\ISecureRandom; abstract class AbstractDatabase { /** @var IL10N */ protected $trans; /** @var string */ protected $dbDefinitionFile; /** @var string */ protected $dbUser; /** @var string */ protected $dbPassword; /** @var string */ protected $dbName; /** @var string */ protected $dbHost; /** @var string */ protected $dbPort; /** @var string */ protected $tablePrefix; /** @var SystemConfig */ protected $config; /** @var ILogger */ protected $logger; /** @var ISecureRandom */ protected $random; public function __construct(IL10N $trans, $dbDefinitionFile, SystemConfig $config, ILogger $logger, ISecureRandom $random) { $this->trans = $trans; $this->dbDefinitionFile = $dbDefinitionFile; $this->config = $config; $this->logger = $logger; $this->random = $random; } public function validate($config) { $errors = array(); if(empty($config['dbuser']) && empty($config['dbname'])) { $errors[] = $this->trans->t("%s enter the database username and name.", array($this->dbprettyname)); } else if(empty($config['dbuser'])) { $errors[] = $this->trans->t("%s enter the database username.", array($this->dbprettyname)); } else if(empty($config['dbname'])) { $errors[] = $this->trans->t("%s enter the database name.", array($this->dbprettyname)); } if(substr_count($config['dbname'], '.') >= 1) { $errors[] = $this->trans->t("%s you may not use dots in the database name", array($this->dbprettyname)); } return $errors; } public function initialize($config) { $dbUser = $config['dbuser']; $dbPass = $config['dbpass']; $dbName = $config['dbname']; $dbHost = !empty($config['dbhost']) ? $config['dbhost'] : 'localhost'; $dbPort = !empty($config['dbport']) ? $config['dbport'] : ''; $dbTablePrefix = isset($config['dbtableprefix']) ? $config['dbtableprefix'] : 'oc_'; $this->config->setValues([ 'dbname' => $dbName, 'dbhost' => $dbHost, 'dbport' => $dbPort, 'dbtableprefix' => $dbTablePrefix, ]); $this->dbUser = $dbUser; $this->dbPassword = $dbPass; $this->dbName = $dbName; $this->dbHost = $dbHost; $this->dbPort = $dbPort; $this->tablePrefix = $dbTablePrefix; } /** * @param array $configOverwrite * @return \OC\DB\Connection */ protected function connect(array $configOverwrite = []) { $connectionParams = array( 'host' => $this->dbHost, 'user' => $this->dbUser, 'password' => $this->dbPassword, 'tablePrefix' => $this->tablePrefix, 'dbname' => $this->dbName ); // adding port support through installer if (!empty($this->dbPort)) { if (ctype_digit($this->dbPort)) { $connectionParams['port'] = $this->dbPort; } else { $connectionParams['unix_socket'] = $this->dbPort; } } else if (strpos($this->dbHost, ':')) { // Host variable may carry a port or socket. list($host, $portOrSocket) = explode(':', $this->dbHost, 2); if (ctype_digit($portOrSocket)) { $connectionParams['port'] = $portOrSocket; } else { $connectionParams['unix_socket'] = $portOrSocket; } $connectionParams['host'] = $host; } $connectionParams = array_merge($connectionParams, $configOverwrite); $cf = new ConnectionFactory($this->config); return $cf->getConnection($this->config->getValue('dbtype', 'sqlite'), $connectionParams); } /** * @param string $userName */ abstract public function setupDatabase($userName); } private/Setup/Sqlite.php 0000604 00000002634 15247130453 0011275 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Morris Jobke <hey@morrisjobke.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Setup; class Sqlite extends AbstractDatabase { public $dbprettyname = 'Sqlite'; public function validate($config) { return array(); } public function initialize($config) { } public function setupDatabase($username) { $datadir = $this->config->getValue('datadirectory', \OC::$SERVERROOT . '/data'); //delete the old sqlite database first, might cause infinte loops otherwise if(file_exists("$datadir/owncloud.db")) { unlink("$datadir/owncloud.db"); } //in case of sqlite, we can always fill the database error_log("creating sqlite db"); \OC_DB::createDbFromStructure($this->dbDefinitionFile); } } private/Setup/MySQL.php 0000604 00000013014 15247130453 0010773 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Michael Göhler <somebody.here@gmx.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Setup; use OC\DB\MySqlTools; use OCP\IDBConnection; class MySQL extends AbstractDatabase { public $dbprettyname = 'MySQL/MariaDB'; public function setupDatabase($username) { //check if the database user has admin right $connection = $this->connect(['dbname' => null]); // detect mb4 $tools = new MySqlTools(); if ($tools->supports4ByteCharset($connection)) { $this->config->setValue('mysql.utf8mb4', true); $connection = $this->connect(['dbname' => null]); } $this->createSpecificUser($username, $connection); //create the database $this->createDatabase($connection); //fill the database if needed $query='select count(*) from information_schema.tables where table_schema=? AND table_name = ?'; $result = $connection->executeQuery($query, [$this->dbName, $this->tablePrefix.'users']); $row = $result->fetch(); if (!$row or $row['count(*)'] === '0') { \OC_DB::createDbFromStructure($this->dbDefinitionFile); } } /** * @param \OC\DB\Connection $connection */ private function createDatabase($connection) { try{ $name = $this->dbName; $user = $this->dbUser; //we can't use OC_DB functions here because we need to connect as the administrative user. $characterSet = $this->config->getValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8'; $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET $characterSet COLLATE ${characterSet}_bin;"; $connection->executeUpdate($query); } catch (\Exception $ex) { $this->logger->error('Database creation failed: {error}', [ 'app' => 'mysql.setup', 'error' => $ex->getMessage() ]); return; } try { //this query will fail if there aren't the right permissions, ignore the error $query="GRANT ALL PRIVILEGES ON `$name` . * TO '$user'"; $connection->executeUpdate($query); } catch (\Exception $ex) { $this->logger->debug('Could not automatically grant privileges, this can be ignored if database user already had privileges: {error}', [ 'app' => 'mysql.setup', 'error' => $ex->getMessage() ]); } } /** * @param IDBConnection $connection * @throws \OC\DatabaseSetupException */ private function createDBUser($connection) { try{ $name = $this->dbUser; $password = $this->dbPassword; // we need to create 2 accounts, one for global use and one for local user. if we don't specify the local one, // the anonymous user would take precedence when there is one. $query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'"; $connection->executeUpdate($query); $query = "CREATE USER '$name'@'%' IDENTIFIED BY '$password'"; $connection->executeUpdate($query); } catch (\Exception $ex){ $this->logger->error('Database User creation failed: {error}', [ 'app' => 'mysql.setup', 'error' => $ex->getMessage() ]); } } /** * @param $username * @param IDBConnection $connection * @return array */ private function createSpecificUser($username, $connection) { try { //user already specified in config $oldUser = $this->config->getValue('dbuser', false); //we don't have a dbuser specified in config if ($this->dbUser !== $oldUser) { //add prefix to the admin username to prevent collisions $adminUser = substr('oc_' . $username, 0, 16); $i = 1; while (true) { //this should be enough to check for admin rights in mysql $query = 'SELECT user FROM mysql.user WHERE user=?'; $result = $connection->executeQuery($query, [$adminUser]); //current dbuser has admin rights if ($result) { $data = $result->fetchAll(); //new dbuser does not exist if (count($data) === 0) { //use the admin login data for the new database user $this->dbUser = $adminUser; //create a random password so we don't need to store the admin password in the config file $this->dbPassword = $this->random->generate(30); $this->createDBUser($connection); break; } else { //repeat with different username $length = strlen((string)$i); $adminUser = substr('oc_' . $username, 0, 16 - $length) . $i; $i++; } } else { break; } }; } } catch (\Exception $ex) { $this->logger->info('Can not create a new MySQL user, will continue with the provided user: {error}', [ 'app' => 'mysql.setup', 'error' => $ex->getMessage() ]); } $this->config->setValues([ 'dbuser' => $this->dbUser, 'dbpassword' => $this->dbPassword, ]); } } private/Setup/OCI.php 0000604 00000025100 15247130453 0010437 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Andreas Fischer <bantu@owncloud.com> * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Manish Bisht <manish.bisht490@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Pulzer <t.pulzer@kniel.de> * @author Victor Dubiniuk <dubiniuk@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\Setup; class OCI extends AbstractDatabase { public $dbprettyname = 'Oracle'; protected $dbtablespace; public function initialize($config) { parent::initialize($config); if (array_key_exists('dbtablespace', $config)) { $this->dbtablespace = $config['dbtablespace']; } else { $this->dbtablespace = 'USERS'; } // allow empty hostname for oracle $this->dbHost = $config['dbhost']; $this->config->setValues([ 'dbhost' => $this->dbHost, 'dbtablespace' => $this->dbtablespace, ]); } public function validate($config) { $errors = array(); if(empty($config['dbuser']) && empty($config['dbname'])) { $errors[] = $this->trans->t("%s enter the database username and name.", array($this->dbprettyname)); } else if(empty($config['dbuser'])) { $errors[] = $this->trans->t("%s enter the database username.", array($this->dbprettyname)); } else if(empty($config['dbname'])) { $errors[] = $this->trans->t("%s enter the database name.", array($this->dbprettyname)); } return $errors; } public function setupDatabase($username) { $e_host = addslashes($this->dbHost); // casting to int to avoid malicious input $e_port = (int)$this->dbPort; $e_dbname = addslashes($this->dbName); //check if the database user has admin right if ($e_host == '') { $easy_connect_string = $e_dbname; // use dbname as easy connect name } else { $easy_connect_string = '//'.$e_host.(!empty($e_port) ? ":{$e_port}" : "").'/'.$e_dbname; } $this->logger->debug('connect string: ' . $easy_connect_string, ['app' => 'setup.oci']); $connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string); if(!$connection) { $errorMessage = $this->getLastError(); if ($errorMessage) { throw new \OC\DatabaseSetupException($this->trans->t('Oracle connection could not be established'), $errorMessage.' Check environment: ORACLE_HOME='.getenv('ORACLE_HOME') .' ORACLE_SID='.getenv('ORACLE_SID') .' LD_LIBRARY_PATH='.getenv('LD_LIBRARY_PATH') .' NLS_LANG='.getenv('NLS_LANG') .' tnsnames.ora is '.(is_readable(getenv('ORACLE_HOME').'/network/admin/tnsnames.ora')?'':'not ').'readable'); } throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'), 'Check environment: ORACLE_HOME='.getenv('ORACLE_HOME') .' ORACLE_SID='.getenv('ORACLE_SID') .' LD_LIBRARY_PATH='.getenv('LD_LIBRARY_PATH') .' NLS_LANG='.getenv('NLS_LANG') .' tnsnames.ora is '.(is_readable(getenv('ORACLE_HOME').'/network/admin/tnsnames.ora')?'':'not ').'readable'); } //check for roles creation rights in oracle $query='SELECT count(*) FROM user_role_privs, role_sys_privs' ." WHERE user_role_privs.granted_role = role_sys_privs.role AND privilege = 'CREATE ROLE'"; $stmt = oci_parse($connection, $query); if (!$stmt) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } $result = oci_execute($stmt); if($result) { $row = oci_fetch_row($stmt); if ($row[0] > 0) { //use the admin login data for the new database user //add prefix to the oracle user name to prevent collisions $this->dbUser='oc_'.$username; //create a new password so we don't need to store the admin config in the config file $this->dbPassword = \OC::$server->getSecureRandom()->generate(30, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS); //oracle passwords are treated as identifiers: // must start with alphanumeric char // needs to be shortened to 30 bytes, as the two " needed to escape the identifier count towards the identifier length. $this->dbPassword=substr($this->dbPassword, 0, 30); $this->createDBUser($connection); } } $this->config->setValues([ 'dbuser' => $this->dbUser, 'dbname' => $this->dbName, 'dbpassword' => $this->dbPassword, ]); //create the database not necessary, oracle implies user = schema //$this->createDatabase($this->dbname, $this->dbuser, $connection); //FIXME check tablespace exists: select * from user_tablespaces // the connection to dbname=oracle is not needed anymore oci_close($connection); // connect to the oracle database (schema=$this->dbuser) an check if the schema needs to be filled $this->dbUser = $this->config->getValue('dbuser'); //$this->dbname = \OC_Config::getValue('dbname'); $this->dbPassword = $this->config->getValue('dbpassword'); $e_host = addslashes($this->dbHost); $e_dbname = addslashes($this->dbName); if ($e_host == '') { $easy_connect_string = $e_dbname; // use dbname as easy connect name } else { $easy_connect_string = '//' . $e_host . (!empty($e_port) ? ":{$e_port}" : "") . '/' . $e_dbname; } $connection = @oci_connect($this->dbUser, $this->dbPassword, $easy_connect_string); if(!$connection) { throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'), $this->trans->t('You need to enter details of an existing account.')); } $query = "SELECT count(*) FROM user_tables WHERE table_name = :un"; $stmt = oci_parse($connection, $query); $un = $this->tablePrefix.'users'; oci_bind_by_name($stmt, ':un', $un); if (!$stmt) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; $this->logger->warning( $entry, ['app' => 'setup.oci']); } $result = oci_execute($stmt); if($result) { $row = oci_fetch_row($stmt); } if(!$result or $row[0]==0) { \OC_DB::createDbFromStructure($this->dbDefinitionFile); } } /** * @param resource $connection */ private function createDBUser($connection) { $name = $this->dbUser; $password = $this->dbPassword; $query = "SELECT * FROM all_users WHERE USERNAME = :un"; $stmt = oci_parse($connection, $query); if (!$stmt) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } oci_bind_by_name($stmt, ':un', $name); $result = oci_execute($stmt); if(!$result) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } if(! oci_fetch_row($stmt)) { //user does not exists let's create it :) //password must start with alphabetic character in oracle $query = 'CREATE USER '.$name.' IDENTIFIED BY "'.$password.'" DEFAULT TABLESPACE '.$this->dbtablespace; $stmt = oci_parse($connection, $query); if (!$stmt) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } //oci_bind_by_name($stmt, ':un', $name); $result = oci_execute($stmt); if(!$result) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s", name: %s, password: %s', array($query, $name, $password)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } } else { // change password of the existing role $query = "ALTER USER :un IDENTIFIED BY :pw"; $stmt = oci_parse($connection, $query); if (!$stmt) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } oci_bind_by_name($stmt, ':un', $name); oci_bind_by_name($stmt, ':pw', $password); $result = oci_execute($stmt); if(!$result) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } } // grant necessary roles $query = 'GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER, UNLIMITED TABLESPACE TO '.$name; $stmt = oci_parse($connection, $query); if (!$stmt) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } $result = oci_execute($stmt); if(!$result) { $entry = $this->trans->t('DB Error: "%s"', array($this->getLastError($connection))) . '<br />'; $entry .= $this->trans->t('Offending command was: "%s", name: %s, password: %s', array($query, $name, $password)) . '<br />'; $this->logger->warning($entry, ['app' => 'setup.oci']); } } /** * @param resource $connection * @return string */ protected function getLastError($connection = null) { if ($connection) { $error = oci_error($connection); } else { $error = oci_error(); } foreach (array('message', 'code') as $key) { if (isset($error[$key])) { return $error[$key]; } } return ''; } } private/AvatarManager.php 0000604 00000005313 15247130453 0011442 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\IAvatarManager; use OCP\IConfig; use OCP\ILogger; use OCP\IUserManager; use OCP\IL10N; /** * This class implements methods to access Avatar functionality */ class AvatarManager implements IAvatarManager { /** @var IUserManager */ private $userManager; /** @var IAppData */ private $appData; /** @var IL10N */ private $l; /** @var ILogger */ private $logger; /** @var IConfig */ private $config; /** * AvatarManager constructor. * * @param IUserManager $userManager * @param IAppData $appData * @param IL10N $l * @param ILogger $logger * @param IConfig $config */ public function __construct( IUserManager $userManager, IAppData $appData, IL10N $l, ILogger $logger, IConfig $config) { $this->userManager = $userManager; $this->appData = $appData; $this->l = $l; $this->logger = $logger; $this->config = $config; } /** * return a user specific instance of \OCP\IAvatar * @see \OCP\IAvatar * @param string $userId the ownCloud user id * @return \OCP\IAvatar * @throws \Exception In case the username is potentially dangerous * @throws NotFoundException In case there is no user folder yet */ public function getAvatar($userId) { $user = $this->userManager->get($userId); if (is_null($user)) { throw new \Exception('user does not exist'); } // sanitize userID - fixes casing issue (needed for the filesystem stuff that is done below) $userId = $user->getUID(); try { $folder = $this->appData->getFolder($userId); } catch (NotFoundException $e) { $folder = $this->appData->newFolder($userId); } return new Avatar($folder, $this->l, $user, $this->logger, $this->config); } } private/BackgroundJob/Legacy/RegularJob.php 0000604 00000002167 15247130453 0014707 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\BackgroundJob\Legacy; use OCP\AutoloadNotAllowedException; class RegularJob extends \OC\BackgroundJob\Job { public function run($argument) { try { if (is_callable($argument)) { call_user_func($argument); } } catch (AutoloadNotAllowedException $e) { // job is from a disabled app, ignore return null; } } } private/BackgroundJob/Legacy/QueuedJob.php 0000604 00000002161 15247130453 0014530 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\BackgroundJob\Legacy; class QueuedJob extends \OC\BackgroundJob\QueuedJob { public function run($argument) { $class = $argument['klass']; $method = $argument['method']; $parameters = $argument['parameters']; if (is_callable(array($class, $method))) { call_user_func(array($class, $method), $parameters); } } } private/BackgroundJob/Job.php 0000604 00000004575 15247130453 0012166 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\BackgroundJob; use OCP\BackgroundJob\IJob; use OCP\ILogger; abstract class Job implements IJob { /** * @var int $id */ protected $id; /** * @var int $lastRun */ protected $lastRun; /** * @var mixed $argument */ protected $argument; /** * @param JobList $jobList * @param ILogger $logger */ public function execute($jobList, ILogger $logger = null) { $jobList->setLastRun($this); if ($logger === null) { $logger = \OC::$server->getLogger(); } try { $jobStartTime = time(); $logger->debug('Run ' . get_class($this) . ' job with ID ' . $this->getId(), ['app' => 'cron']); $this->run($this->argument); $timeTaken = time() - $jobStartTime; $logger->debug('Finished ' . get_class($this) . ' job with ID ' . $this->getId() . ' in ' . $timeTaken . ' seconds', ['app' => 'cron']); $jobList->setExecutionTime($this, $timeTaken); } catch (\Exception $e) { if ($logger) { $logger->logException($e, [ 'app' => 'core', 'message' => 'Error while running background job (class: ' . get_class($this) . ', arguments: ' . print_r($this->argument, true) . ')' ]); } } } abstract protected function run($argument); public function setId($id) { $this->id = $id; } public function setLastRun($lastRun) { $this->lastRun = $lastRun; } public function setArgument($argument) { $this->argument = $argument; } public function getId() { return $this->id; } public function getLastRun() { return $this->lastRun; } public function getArgument() { return $this->argument; } } private/BackgroundJob/QueuedJob.php 0000604 00000002362 15247130453 0013327 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\BackgroundJob; use OCP\ILogger; /** * Class QueuedJob * * create a background job that is to be executed once * * @package OC\BackgroundJob */ abstract class QueuedJob extends Job { /** * run the job, then remove it from the joblist * * @param JobList $jobList * @param ILogger $logger */ public function execute($jobList, ILogger $logger = null) { $jobList->remove($this, $this->argument); parent::execute($jobList, $logger); } } private/BackgroundJob/JobList.php 0000604 00000021662 15247130453 0013016 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\BackgroundJob; use OCP\AppFramework\QueryException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\IJobList; use OCP\AutoloadNotAllowedException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; class JobList implements IJobList { /** @var IDBConnection */ protected $connection; /**@var IConfig */ protected $config; /**@var ITimeFactory */ protected $timeFactory; /** * @param IDBConnection $connection * @param IConfig $config * @param ITimeFactory $timeFactory */ public function __construct(IDBConnection $connection, IConfig $config, ITimeFactory $timeFactory) { $this->connection = $connection; $this->config = $config; $this->timeFactory = $timeFactory; } /** * @param IJob|string $job * @param mixed $argument */ public function add($job, $argument = null) { if (!$this->has($job, $argument)) { if ($job instanceof IJob) { $class = get_class($job); } else { $class = $job; } $argument = json_encode($argument); if (strlen($argument) > 4000) { throw new \InvalidArgumentException('Background job arguments can\'t exceed 4000 characters (json encoded)'); } $query = $this->connection->getQueryBuilder(); $query->insert('jobs') ->values([ 'class' => $query->createNamedParameter($class), 'argument' => $query->createNamedParameter($argument), 'last_run' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT), 'last_checked' => $query->createNamedParameter($this->timeFactory->getTime(), IQueryBuilder::PARAM_INT), ]); $query->execute(); } } /** * @param IJob|string $job * @param mixed $argument */ public function remove($job, $argument = null) { if ($job instanceof IJob) { $class = get_class($job); } else { $class = $job; } $query = $this->connection->getQueryBuilder(); $query->delete('jobs') ->where($query->expr()->eq('class', $query->createNamedParameter($class))); if (!is_null($argument)) { $argument = json_encode($argument); $query->andWhere($query->expr()->eq('argument', $query->createNamedParameter($argument))); } $query->execute(); } /** * @param int $id */ protected function removeById($id) { $query = $this->connection->getQueryBuilder(); $query->delete('jobs') ->where($query->expr()->eq('id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $query->execute(); } /** * check if a job is in the list * * @param IJob|string $job * @param mixed $argument * @return bool */ public function has($job, $argument) { if ($job instanceof IJob) { $class = get_class($job); } else { $class = $job; } $argument = json_encode($argument); $query = $this->connection->getQueryBuilder(); $query->select('id') ->from('jobs') ->where($query->expr()->eq('class', $query->createNamedParameter($class))) ->andWhere($query->expr()->eq('argument', $query->createNamedParameter($argument))) ->setMaxResults(1); $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); return (bool) $row; } /** * get all jobs in the list * * @return IJob[] * @deprecated 9.0.0 - This method is dangerous since it can cause load and * memory problems when creating too many instances. */ public function getAll() { $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('jobs'); $result = $query->execute(); $jobs = []; while ($row = $result->fetch()) { $job = $this->buildJob($row); if ($job) { $jobs[] = $job; } } $result->closeCursor(); return $jobs; } /** * get the next job in the list * * @return IJob|null */ public function getNext() { $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('jobs') ->where($query->expr()->lte('reserved_at', $query->createNamedParameter($this->timeFactory->getTime() - 12 * 3600, IQueryBuilder::PARAM_INT))) ->orderBy('last_checked', 'ASC') ->setMaxResults(1); $update = $this->connection->getQueryBuilder(); $update->update('jobs') ->set('reserved_at', $update->createNamedParameter($this->timeFactory->getTime())) ->set('last_checked', $update->createNamedParameter($this->timeFactory->getTime())) ->where($update->expr()->eq('id', $update->createParameter('jobid'))) ->andWhere($update->expr()->eq('reserved_at', $update->createParameter('reserved_at'))) ->andWhere($update->expr()->eq('last_checked', $update->createParameter('last_checked'))); $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); if ($row) { $update->setParameter('jobid', $row['id']); $update->setParameter('reserved_at', $row['reserved_at']); $update->setParameter('last_checked', $row['last_checked']); $count = $update->execute(); if ($count === 0) { // Background job already executed elsewhere, try again. return $this->getNext(); } $job = $this->buildJob($row); if ($job === null) { // Background job from disabled app, try again. return $this->getNext(); } return $job; } else { return null; } } /** * @param int $id * @return IJob|null */ public function getById($id) { $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('jobs') ->where($query->expr()->eq('id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); $result = $query->execute(); $row = $result->fetch(); $result->closeCursor(); if ($row) { return $this->buildJob($row); } else { return null; } } /** * get the job object from a row in the db * * @param array $row * @return IJob|null */ private function buildJob($row) { try { try { // Try to load the job as a service /** @var IJob $job */ $job = \OC::$server->query($row['class']); } catch (QueryException $e) { if (class_exists($row['class'])) { $class = $row['class']; $job = new $class(); } else { // job from disabled app or old version of an app, no need to do anything return null; } } $job->setId($row['id']); $job->setLastRun($row['last_run']); $job->setArgument(json_decode($row['argument'], true)); return $job; } catch (AutoloadNotAllowedException $e) { // job is from a disabled app, ignore return null; } } /** * set the job that was last ran * * @param IJob $job */ public function setLastJob(IJob $job) { $this->unlockJob($job); $this->config->setAppValue('backgroundjob', 'lastjob', $job->getId()); } /** * Remove the reservation for a job * * @param IJob $job */ public function unlockJob(IJob $job) { $query = $this->connection->getQueryBuilder(); $query->update('jobs') ->set('reserved_at', $query->expr()->literal(0, IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT))); $query->execute(); } /** * get the id of the last ran job * * @return int * @deprecated 9.1.0 - The functionality behind the value is deprecated, it * only tells you which job finished last, but since we now allow multiple * executors to run in parallel, it's not used to calculate the next job. */ public function getLastJob() { return (int) $this->config->getAppValue('backgroundjob', 'lastjob', 0); } /** * set the lastRun of $job to now * * @param IJob $job */ public function setLastRun(IJob $job) { $query = $this->connection->getQueryBuilder(); $query->update('jobs') ->set('last_run', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT))); $query->execute(); } /** * @param IJob $job * @param $timeTaken */ public function setExecutionTime(IJob $job, $timeTaken) { $query = $this->connection->getQueryBuilder(); $query->update('jobs') ->set('execution_duration', $query->createNamedParameter($timeTaken, IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT))); $query->execute(); } } private/BackgroundJob/TimedJob.php 0000604 00000002633 15247130453 0013142 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OC\BackgroundJob; use OCP\ILogger; /** * Class QueuedJob * * create a background job that is to be executed at an interval * * @package OC\BackgroundJob */ abstract class TimedJob extends Job { protected $interval = 0; /** * set the interval for the job * * @param int $interval */ public function setInterval($interval) { $this->interval = $interval; } /** * run the job if * * @param JobList $jobList * @param ILogger $logger */ public function execute($jobList, ILogger $logger = null) { if ((time() - $this->lastRun) > $this->interval) { parent::execute($jobList, $logger); } } } composer/composer/autoload_namespaces.php 0000604 00000000236 15247130453 0014743 0 ustar 00 <?php // autoload_namespaces.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname(dirname($vendorDir)); return array( ); composer/composer/autoload_classmap.php 0000604 00000242571 15247130453 0014441 0 ustar 00 <?php // autoload_classmap.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname(dirname($vendorDir)); return array( 'OCP\\API' => $baseDir . '/lib/public/API.php', 'OCP\\Activity\\IConsumer' => $baseDir . '/lib/public/Activity/IConsumer.php', 'OCP\\Activity\\IEvent' => $baseDir . '/lib/public/Activity/IEvent.php', 'OCP\\Activity\\IEventMerger' => $baseDir . '/lib/public/Activity/IEventMerger.php', 'OCP\\Activity\\IExtension' => $baseDir . '/lib/public/Activity/IExtension.php', 'OCP\\Activity\\IFilter' => $baseDir . '/lib/public/Activity/IFilter.php', 'OCP\\Activity\\IManager' => $baseDir . '/lib/public/Activity/IManager.php', 'OCP\\Activity\\IProvider' => $baseDir . '/lib/public/Activity/IProvider.php', 'OCP\\Activity\\ISetting' => $baseDir . '/lib/public/Activity/ISetting.php', 'OCP\\App' => $baseDir . '/lib/public/App.php', 'OCP\\AppFramework\\ApiController' => $baseDir . '/lib/public/AppFramework/ApiController.php', 'OCP\\AppFramework\\App' => $baseDir . '/lib/public/AppFramework/App.php', 'OCP\\AppFramework\\Controller' => $baseDir . '/lib/public/AppFramework/Controller.php', 'OCP\\AppFramework\\Db\\DoesNotExistException' => $baseDir . '/lib/public/AppFramework/Db/DoesNotExistException.php', 'OCP\\AppFramework\\Db\\Entity' => $baseDir . '/lib/public/AppFramework/Db/Entity.php', 'OCP\\AppFramework\\Db\\Mapper' => $baseDir . '/lib/public/AppFramework/Db/Mapper.php', 'OCP\\AppFramework\\Db\\MultipleObjectsReturnedException' => $baseDir . '/lib/public/AppFramework/Db/MultipleObjectsReturnedException.php', 'OCP\\AppFramework\\Http' => $baseDir . '/lib/public/AppFramework/Http.php', 'OCP\\AppFramework\\Http\\ContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/ContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\DataDisplayResponse' => $baseDir . '/lib/public/AppFramework/Http/DataDisplayResponse.php', 'OCP\\AppFramework\\Http\\DataDownloadResponse' => $baseDir . '/lib/public/AppFramework/Http/DataDownloadResponse.php', 'OCP\\AppFramework\\Http\\DataResponse' => $baseDir . '/lib/public/AppFramework/Http/DataResponse.php', 'OCP\\AppFramework\\Http\\DownloadResponse' => $baseDir . '/lib/public/AppFramework/Http/DownloadResponse.php', 'OCP\\AppFramework\\Http\\EmptyContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\FileDisplayResponse' => $baseDir . '/lib/public/AppFramework/Http/FileDisplayResponse.php', 'OCP\\AppFramework\\Http\\ICallbackResponse' => $baseDir . '/lib/public/AppFramework/Http/ICallbackResponse.php', 'OCP\\AppFramework\\Http\\IOutput' => $baseDir . '/lib/public/AppFramework/Http/IOutput.php', 'OCP\\AppFramework\\Http\\JSONResponse' => $baseDir . '/lib/public/AppFramework/Http/JSONResponse.php', 'OCP\\AppFramework\\Http\\NotFoundResponse' => $baseDir . '/lib/public/AppFramework/Http/NotFoundResponse.php', 'OCP\\AppFramework\\Http\\OCSResponse' => $baseDir . '/lib/public/AppFramework/Http/OCSResponse.php', 'OCP\\AppFramework\\Http\\RedirectResponse' => $baseDir . '/lib/public/AppFramework/Http/RedirectResponse.php', 'OCP\\AppFramework\\Http\\Response' => $baseDir . '/lib/public/AppFramework/Http/Response.php', 'OCP\\AppFramework\\Http\\StreamResponse' => $baseDir . '/lib/public/AppFramework/Http/StreamResponse.php', 'OCP\\AppFramework\\Http\\TemplateResponse' => $baseDir . '/lib/public/AppFramework/Http/TemplateResponse.php', 'OCP\\AppFramework\\IApi' => $baseDir . '/lib/public/AppFramework/IApi.php', 'OCP\\AppFramework\\IAppContainer' => $baseDir . '/lib/public/AppFramework/IAppContainer.php', 'OCP\\AppFramework\\Middleware' => $baseDir . '/lib/public/AppFramework/Middleware.php', 'OCP\\AppFramework\\OCSController' => $baseDir . '/lib/public/AppFramework/OCSController.php', 'OCP\\AppFramework\\OCS\\OCSBadRequestException' => $baseDir . '/lib/public/AppFramework/OCS/OCSBadRequestException.php', 'OCP\\AppFramework\\OCS\\OCSException' => $baseDir . '/lib/public/AppFramework/OCS/OCSException.php', 'OCP\\AppFramework\\OCS\\OCSForbiddenException' => $baseDir . '/lib/public/AppFramework/OCS/OCSForbiddenException.php', 'OCP\\AppFramework\\OCS\\OCSNotFoundException' => $baseDir . '/lib/public/AppFramework/OCS/OCSNotFoundException.php', 'OCP\\AppFramework\\QueryException' => $baseDir . '/lib/public/AppFramework/QueryException.php', 'OCP\\AppFramework\\Utility\\IControllerMethodReflector' => $baseDir . '/lib/public/AppFramework/Utility/IControllerMethodReflector.php', 'OCP\\AppFramework\\Utility\\ITimeFactory' => $baseDir . '/lib/public/AppFramework/Utility/ITimeFactory.php', 'OCP\\App\\AppPathNotFoundException' => $baseDir . '/lib/public/App/AppPathNotFoundException.php', 'OCP\\App\\IAppManager' => $baseDir . '/lib/public/App/IAppManager.php', 'OCP\\App\\ManagerEvent' => $baseDir . '/lib/public/App/ManagerEvent.php', 'OCP\\Authentication\\Exceptions\\CredentialsUnavailableException' => $baseDir . '/lib/public/Authentication/Exceptions/CredentialsUnavailableException.php', 'OCP\\Authentication\\Exceptions\\PasswordUnavailableException' => $baseDir . '/lib/public/Authentication/Exceptions/PasswordUnavailableException.php', 'OCP\\Authentication\\IApacheBackend' => $baseDir . '/lib/public/Authentication/IApacheBackend.php', 'OCP\\Authentication\\LoginCredentials\\ICredentials' => $baseDir . '/lib/public/Authentication/LoginCredentials/ICredentials.php', 'OCP\\Authentication\\LoginCredentials\\IStore' => $baseDir . '/lib/public/Authentication/LoginCredentials/IStore.php', 'OCP\\Authentication\\TwoFactorAuth\\IProvider' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/IProvider.php', 'OCP\\Authentication\\TwoFactorAuth\\TwoFactorException' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/TwoFactorException.php', 'OCP\\AutoloadNotAllowedException' => $baseDir . '/lib/public/AutoloadNotAllowedException.php', 'OCP\\BackgroundJob' => $baseDir . '/lib/public/BackgroundJob.php', 'OCP\\BackgroundJob\\IJob' => $baseDir . '/lib/public/BackgroundJob/IJob.php', 'OCP\\BackgroundJob\\IJobList' => $baseDir . '/lib/public/BackgroundJob/IJobList.php', 'OCP\\Capabilities\\ICapability' => $baseDir . '/lib/public/Capabilities/ICapability.php', 'OCP\\Command\\IBus' => $baseDir . '/lib/public/Command/IBus.php', 'OCP\\Command\\ICommand' => $baseDir . '/lib/public/Command/ICommand.php', 'OCP\\Comments\\CommentsEntityEvent' => $baseDir . '/lib/public/Comments/CommentsEntityEvent.php', 'OCP\\Comments\\CommentsEvent' => $baseDir . '/lib/public/Comments/CommentsEvent.php', 'OCP\\Comments\\IComment' => $baseDir . '/lib/public/Comments/IComment.php', 'OCP\\Comments\\ICommentsEventHandler' => $baseDir . '/lib/public/Comments/ICommentsEventHandler.php', 'OCP\\Comments\\ICommentsManager' => $baseDir . '/lib/public/Comments/ICommentsManager.php', 'OCP\\Comments\\ICommentsManagerFactory' => $baseDir . '/lib/public/Comments/ICommentsManagerFactory.php', 'OCP\\Comments\\IllegalIDChangeException' => $baseDir . '/lib/public/Comments/IllegalIDChangeException.php', 'OCP\\Comments\\MessageTooLongException' => $baseDir . '/lib/public/Comments/MessageTooLongException.php', 'OCP\\Comments\\NotFoundException' => $baseDir . '/lib/public/Comments/NotFoundException.php', 'OCP\\Config' => $baseDir . '/lib/public/Config.php', 'OCP\\Console\\ConsoleEvent' => $baseDir . '/lib/public/Console/ConsoleEvent.php', 'OCP\\Constants' => $baseDir . '/lib/public/Constants.php', 'OCP\\Contacts' => $baseDir . '/lib/public/Contacts.php', 'OCP\\Contacts\\ContactsMenu\\IAction' => $baseDir . '/lib/public/Contacts/ContactsMenu/IAction.php', 'OCP\\Contacts\\ContactsMenu\\IActionFactory' => $baseDir . '/lib/public/Contacts/ContactsMenu/IActionFactory.php', 'OCP\\Contacts\\ContactsMenu\\IEntry' => $baseDir . '/lib/public/Contacts/ContactsMenu/IEntry.php', 'OCP\\Contacts\\ContactsMenu\\ILinkAction' => $baseDir . '/lib/public/Contacts/ContactsMenu/ILinkAction.php', 'OCP\\Contacts\\ContactsMenu\\IProvider' => $baseDir . '/lib/public/Contacts/ContactsMenu/IProvider.php', 'OCP\\Contacts\\IManager' => $baseDir . '/lib/public/Contacts/IManager.php', 'OCP\\DB' => $baseDir . '/lib/public/DB.php', 'OCP\\DB\\QueryBuilder\\ICompositeExpression' => $baseDir . '/lib/public/DB/QueryBuilder/ICompositeExpression.php', 'OCP\\DB\\QueryBuilder\\IExpressionBuilder' => $baseDir . '/lib/public/DB/QueryBuilder/IExpressionBuilder.php', 'OCP\\DB\\QueryBuilder\\IFunctionBuilder' => $baseDir . '/lib/public/DB/QueryBuilder/IFunctionBuilder.php', 'OCP\\DB\\QueryBuilder\\ILiteral' => $baseDir . '/lib/public/DB/QueryBuilder/ILiteral.php', 'OCP\\DB\\QueryBuilder\\IParameter' => $baseDir . '/lib/public/DB/QueryBuilder/IParameter.php', 'OCP\\DB\\QueryBuilder\\IQueryBuilder' => $baseDir . '/lib/public/DB/QueryBuilder/IQueryBuilder.php', 'OCP\\DB\\QueryBuilder\\IQueryFunction' => $baseDir . '/lib/public/DB/QueryBuilder/IQueryFunction.php', 'OCP\\Defaults' => $baseDir . '/lib/public/Defaults.php', 'OCP\\Diagnostics\\IEvent' => $baseDir . '/lib/public/Diagnostics/IEvent.php', 'OCP\\Diagnostics\\IEventLogger' => $baseDir . '/lib/public/Diagnostics/IEventLogger.php', 'OCP\\Diagnostics\\IQuery' => $baseDir . '/lib/public/Diagnostics/IQuery.php', 'OCP\\Diagnostics\\IQueryLogger' => $baseDir . '/lib/public/Diagnostics/IQueryLogger.php', 'OCP\\Encryption\\Exceptions\\GenericEncryptionException' => $baseDir . '/lib/public/Encryption/Exceptions/GenericEncryptionException.php', 'OCP\\Encryption\\IEncryptionModule' => $baseDir . '/lib/public/Encryption/IEncryptionModule.php', 'OCP\\Encryption\\IFile' => $baseDir . '/lib/public/Encryption/IFile.php', 'OCP\\Encryption\\IManager' => $baseDir . '/lib/public/Encryption/IManager.php', 'OCP\\Encryption\\Keys\\IStorage' => $baseDir . '/lib/public/Encryption/Keys/IStorage.php', 'OCP\\Federation\\ICloudId' => $baseDir . '/lib/public/Federation/ICloudId.php', 'OCP\\Federation\\ICloudIdManager' => $baseDir . '/lib/public/Federation/ICloudIdManager.php', 'OCP\\Files' => $baseDir . '/lib/public/Files.php', 'OCP\\Files\\AlreadyExistsException' => $baseDir . '/lib/public/Files/AlreadyExistsException.php', 'OCP\\Files\\Cache\\ICache' => $baseDir . '/lib/public/Files/Cache/ICache.php', 'OCP\\Files\\Cache\\ICacheEntry' => $baseDir . '/lib/public/Files/Cache/ICacheEntry.php', 'OCP\\Files\\Cache\\IPropagator' => $baseDir . '/lib/public/Files/Cache/IPropagator.php', 'OCP\\Files\\Cache\\IScanner' => $baseDir . '/lib/public/Files/Cache/IScanner.php', 'OCP\\Files\\Cache\\IUpdater' => $baseDir . '/lib/public/Files/Cache/IUpdater.php', 'OCP\\Files\\Cache\\IWatcher' => $baseDir . '/lib/public/Files/Cache/IWatcher.php', 'OCP\\Files\\Config\\ICachedMountInfo' => $baseDir . '/lib/public/Files/Config/ICachedMountInfo.php', 'OCP\\Files\\Config\\IHomeMountProvider' => $baseDir . '/lib/public/Files/Config/IHomeMountProvider.php', 'OCP\\Files\\Config\\IMountProvider' => $baseDir . '/lib/public/Files/Config/IMountProvider.php', 'OCP\\Files\\Config\\IMountProviderCollection' => $baseDir . '/lib/public/Files/Config/IMountProviderCollection.php', 'OCP\\Files\\Config\\IUserMountCache' => $baseDir . '/lib/public/Files/Config/IUserMountCache.php', 'OCP\\Files\\EmptyFileNameException' => $baseDir . '/lib/public/Files/EmptyFileNameException.php', 'OCP\\Files\\EntityTooLargeException' => $baseDir . '/lib/public/Files/EntityTooLargeException.php', 'OCP\\Files\\File' => $baseDir . '/lib/public/Files/File.php', 'OCP\\Files\\FileInfo' => $baseDir . '/lib/public/Files/FileInfo.php', 'OCP\\Files\\FileNameTooLongException' => $baseDir . '/lib/public/Files/FileNameTooLongException.php', 'OCP\\Files\\Folder' => $baseDir . '/lib/public/Files/Folder.php', 'OCP\\Files\\ForbiddenException' => $baseDir . '/lib/public/Files/ForbiddenException.php', 'OCP\\Files\\IAppData' => $baseDir . '/lib/public/Files/IAppData.php', 'OCP\\Files\\IHomeStorage' => $baseDir . '/lib/public/Files/IHomeStorage.php', 'OCP\\Files\\IMimeTypeDetector' => $baseDir . '/lib/public/Files/IMimeTypeDetector.php', 'OCP\\Files\\IMimeTypeLoader' => $baseDir . '/lib/public/Files/IMimeTypeLoader.php', 'OCP\\Files\\IRootFolder' => $baseDir . '/lib/public/Files/IRootFolder.php', 'OCP\\Files\\InvalidCharacterInPathException' => $baseDir . '/lib/public/Files/InvalidCharacterInPathException.php', 'OCP\\Files\\InvalidContentException' => $baseDir . '/lib/public/Files/InvalidContentException.php', 'OCP\\Files\\InvalidDirectoryException' => $baseDir . '/lib/public/Files/InvalidDirectoryException.php', 'OCP\\Files\\InvalidPathException' => $baseDir . '/lib/public/Files/InvalidPathException.php', 'OCP\\Files\\LockNotAcquiredException' => $baseDir . '/lib/public/Files/LockNotAcquiredException.php', 'OCP\\Files\\Mount\\IMountManager' => $baseDir . '/lib/public/Files/Mount/IMountManager.php', 'OCP\\Files\\Mount\\IMountPoint' => $baseDir . '/lib/public/Files/Mount/IMountPoint.php', 'OCP\\Files\\Node' => $baseDir . '/lib/public/Files/Node.php', 'OCP\\Files\\NotEnoughSpaceException' => $baseDir . '/lib/public/Files/NotEnoughSpaceException.php', 'OCP\\Files\\NotFoundException' => $baseDir . '/lib/public/Files/NotFoundException.php', 'OCP\\Files\\NotPermittedException' => $baseDir . '/lib/public/Files/NotPermittedException.php', 'OCP\\Files\\Notify\\IChange' => $baseDir . '/lib/public/Files/Notify/IChange.php', 'OCP\\Files\\Notify\\INotifyHandler' => $baseDir . '/lib/public/Files/Notify/INotifyHandler.php', 'OCP\\Files\\Notify\\IRenameChange' => $baseDir . '/lib/public/Files/Notify/IRenameChange.php', 'OCP\\Files\\ObjectStore\\IObjectStore' => $baseDir . '/lib/public/Files/ObjectStore/IObjectStore.php', 'OCP\\Files\\ReservedWordException' => $baseDir . '/lib/public/Files/ReservedWordException.php', 'OCP\\Files\\Search\\ISearchBinaryOperator' => $baseDir . '/lib/public/Files/Search/ISearchBinaryOperator.php', 'OCP\\Files\\Search\\ISearchComparison' => $baseDir . '/lib/public/Files/Search/ISearchComparison.php', 'OCP\\Files\\Search\\ISearchOperator' => $baseDir . '/lib/public/Files/Search/ISearchOperator.php', 'OCP\\Files\\Search\\ISearchOrder' => $baseDir . '/lib/public/Files/Search/ISearchOrder.php', 'OCP\\Files\\Search\\ISearchQuery' => $baseDir . '/lib/public/Files/Search/ISearchQuery.php', 'OCP\\Files\\SimpleFS\\ISimpleFile' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleFile.php', 'OCP\\Files\\SimpleFS\\ISimpleFolder' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleFolder.php', 'OCP\\Files\\SimpleFS\\ISimpleRoot' => $baseDir . '/lib/public/Files/SimpleFS/ISimpleRoot.php', 'OCP\\Files\\Storage' => $baseDir . '/lib/public/Files/Storage.php', 'OCP\\Files\\StorageAuthException' => $baseDir . '/lib/public/Files/StorageAuthException.php', 'OCP\\Files\\StorageBadConfigException' => $baseDir . '/lib/public/Files/StorageBadConfigException.php', 'OCP\\Files\\StorageConnectionException' => $baseDir . '/lib/public/Files/StorageConnectionException.php', 'OCP\\Files\\StorageInvalidException' => $baseDir . '/lib/public/Files/StorageInvalidException.php', 'OCP\\Files\\StorageNotAvailableException' => $baseDir . '/lib/public/Files/StorageNotAvailableException.php', 'OCP\\Files\\StorageTimeoutException' => $baseDir . '/lib/public/Files/StorageTimeoutException.php', 'OCP\\Files\\Storage\\ILockingStorage' => $baseDir . '/lib/public/Files/Storage/ILockingStorage.php', 'OCP\\Files\\Storage\\INotifyStorage' => $baseDir . '/lib/public/Files/Storage/INotifyStorage.php', 'OCP\\Files\\Storage\\IStorage' => $baseDir . '/lib/public/Files/Storage/IStorage.php', 'OCP\\Files\\Storage\\IStorageFactory' => $baseDir . '/lib/public/Files/Storage/IStorageFactory.php', 'OCP\\Files\\UnseekableException' => $baseDir . '/lib/public/Files/UnseekableException.php', 'OCP\\GlobalScale\\IConfig' => $baseDir . '/lib/public/GlobalScale/IConfig.php', 'OCP\\GroupInterface' => $baseDir . '/lib/public/GroupInterface.php', 'OCP\\Http\\Client\\IClient' => $baseDir . '/lib/public/Http/Client/IClient.php', 'OCP\\Http\\Client\\IClientService' => $baseDir . '/lib/public/Http/Client/IClientService.php', 'OCP\\Http\\Client\\IResponse' => $baseDir . '/lib/public/Http/Client/IResponse.php', 'OCP\\IAddressBook' => $baseDir . '/lib/public/IAddressBook.php', 'OCP\\IAppConfig' => $baseDir . '/lib/public/IAppConfig.php', 'OCP\\IAvatar' => $baseDir . '/lib/public/IAvatar.php', 'OCP\\IAvatarManager' => $baseDir . '/lib/public/IAvatarManager.php', 'OCP\\ICache' => $baseDir . '/lib/public/ICache.php', 'OCP\\ICacheFactory' => $baseDir . '/lib/public/ICacheFactory.php', 'OCP\\ICertificate' => $baseDir . '/lib/public/ICertificate.php', 'OCP\\ICertificateManager' => $baseDir . '/lib/public/ICertificateManager.php', 'OCP\\IConfig' => $baseDir . '/lib/public/IConfig.php', 'OCP\\IContainer' => $baseDir . '/lib/public/IContainer.php', 'OCP\\IDBConnection' => $baseDir . '/lib/public/IDBConnection.php', 'OCP\\IDateTimeFormatter' => $baseDir . '/lib/public/IDateTimeFormatter.php', 'OCP\\IDateTimeZone' => $baseDir . '/lib/public/IDateTimeZone.php', 'OCP\\IEventSource' => $baseDir . '/lib/public/IEventSource.php', 'OCP\\IGroup' => $baseDir . '/lib/public/IGroup.php', 'OCP\\IGroupManager' => $baseDir . '/lib/public/IGroupManager.php', 'OCP\\IHelper' => $baseDir . '/lib/public/IHelper.php', 'OCP\\IImage' => $baseDir . '/lib/public/IImage.php', 'OCP\\IL10N' => $baseDir . '/lib/public/IL10N.php', 'OCP\\ILogger' => $baseDir . '/lib/public/ILogger.php', 'OCP\\IMemcache' => $baseDir . '/lib/public/IMemcache.php', 'OCP\\IMemcacheTTL' => $baseDir . '/lib/public/IMemcacheTTL.php', 'OCP\\INavigationManager' => $baseDir . '/lib/public/INavigationManager.php', 'OCP\\IPreview' => $baseDir . '/lib/public/IPreview.php', 'OCP\\IRequest' => $baseDir . '/lib/public/IRequest.php', 'OCP\\ISearch' => $baseDir . '/lib/public/ISearch.php', 'OCP\\IServerContainer' => $baseDir . '/lib/public/IServerContainer.php', 'OCP\\ISession' => $baseDir . '/lib/public/ISession.php', 'OCP\\ITagManager' => $baseDir . '/lib/public/ITagManager.php', 'OCP\\ITags' => $baseDir . '/lib/public/ITags.php', 'OCP\\ITempManager' => $baseDir . '/lib/public/ITempManager.php', 'OCP\\IURLGenerator' => $baseDir . '/lib/public/IURLGenerator.php', 'OCP\\IUser' => $baseDir . '/lib/public/IUser.php', 'OCP\\IUserBackend' => $baseDir . '/lib/public/IUserBackend.php', 'OCP\\IUserManager' => $baseDir . '/lib/public/IUserManager.php', 'OCP\\IUserSession' => $baseDir . '/lib/public/IUserSession.php', 'OCP\\Image' => $baseDir . '/lib/public/Image.php', 'OCP\\JSON' => $baseDir . '/lib/public/JSON.php', 'OCP\\L10N\\IFactory' => $baseDir . '/lib/public/L10N/IFactory.php', 'OCP\\LDAP\\IDeletionFlagSupport' => $baseDir . '/lib/public/LDAP/IDeletionFlagSupport.php', 'OCP\\LDAP\\ILDAPProvider' => $baseDir . '/lib/public/LDAP/ILDAPProvider.php', 'OCP\\LDAP\\ILDAPProviderFactory' => $baseDir . '/lib/public/LDAP/ILDAPProviderFactory.php', 'OCP\\Lock\\ILockingProvider' => $baseDir . '/lib/public/Lock/ILockingProvider.php', 'OCP\\Lock\\LockedException' => $baseDir . '/lib/public/Lock/LockedException.php', 'OCP\\Lockdown\\ILockdownManager' => $baseDir . '/lib/public/Lockdown/ILockdownManager.php', 'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php', 'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php', 'OCP\\Migration\\IOutput' => $baseDir . '/lib/public/Migration/IOutput.php', 'OCP\\Migration\\IRepairStep' => $baseDir . '/lib/public/Migration/IRepairStep.php', 'OCP\\Notification\\IAction' => $baseDir . '/lib/public/Notification/IAction.php', 'OCP\\Notification\\IApp' => $baseDir . '/lib/public/Notification/IApp.php', 'OCP\\Notification\\IManager' => $baseDir . '/lib/public/Notification/IManager.php', 'OCP\\Notification\\INotification' => $baseDir . '/lib/public/Notification/INotification.php', 'OCP\\Notification\\INotifier' => $baseDir . '/lib/public/Notification/INotifier.php', 'OCP\\OCS\\IDiscoveryService' => $baseDir . '/lib/public/OCS/IDiscoveryService.php', 'OCP\\PreConditionNotMetException' => $baseDir . '/lib/public/PreConditionNotMetException.php', 'OCP\\Preview\\IProvider' => $baseDir . '/lib/public/Preview/IProvider.php', 'OCP\\Response' => $baseDir . '/lib/public/Response.php', 'OCP\\RichObjectStrings\\Definitions' => $baseDir . '/lib/public/RichObjectStrings/Definitions.php', 'OCP\\RichObjectStrings\\IValidator' => $baseDir . '/lib/public/RichObjectStrings/IValidator.php', 'OCP\\RichObjectStrings\\InvalidObjectExeption' => $baseDir . '/lib/public/RichObjectStrings/InvalidObjectExeption.php', 'OCP\\Route\\IRoute' => $baseDir . '/lib/public/Route/IRoute.php', 'OCP\\Route\\IRouter' => $baseDir . '/lib/public/Route/IRouter.php', 'OCP\\SabrePluginEvent' => $baseDir . '/lib/public/SabrePluginEvent.php', 'OCP\\SabrePluginException' => $baseDir . '/lib/public/SabrePluginException.php', 'OCP\\Search\\PagedProvider' => $baseDir . '/lib/public/Search/PagedProvider.php', 'OCP\\Search\\Provider' => $baseDir . '/lib/public/Search/Provider.php', 'OCP\\Search\\Result' => $baseDir . '/lib/public/Search/Result.php', 'OCP\\Security\\IContentSecurityPolicyManager' => $baseDir . '/lib/public/Security/IContentSecurityPolicyManager.php', 'OCP\\Security\\ICredentialsManager' => $baseDir . '/lib/public/Security/ICredentialsManager.php', 'OCP\\Security\\ICrypto' => $baseDir . '/lib/public/Security/ICrypto.php', 'OCP\\Security\\IHasher' => $baseDir . '/lib/public/Security/IHasher.php', 'OCP\\Security\\ISecureRandom' => $baseDir . '/lib/public/Security/ISecureRandom.php', 'OCP\\Security\\StringUtils' => $baseDir . '/lib/public/Security/StringUtils.php', 'OCP\\Session\\Exceptions\\SessionNotAvailableException' => $baseDir . '/lib/public/Session/Exceptions/SessionNotAvailableException.php', 'OCP\\Settings\\IIconSection' => $baseDir . '/lib/public/Settings/IIconSection.php', 'OCP\\Settings\\IManager' => $baseDir . '/lib/public/Settings/IManager.php', 'OCP\\Settings\\ISection' => $baseDir . '/lib/public/Settings/ISection.php', 'OCP\\Settings\\ISettings' => $baseDir . '/lib/public/Settings/ISettings.php', 'OCP\\Share' => $baseDir . '/lib/public/Share.php', 'OCP\\Share\\Exceptions\\GenericShareException' => $baseDir . '/lib/public/Share/Exceptions/GenericShareException.php', 'OCP\\Share\\Exceptions\\IllegalIDChangeException' => $baseDir . '/lib/public/Share/Exceptions/IllegalIDChangeException.php', 'OCP\\Share\\Exceptions\\ShareNotFound' => $baseDir . '/lib/public/Share/Exceptions/ShareNotFound.php', 'OCP\\Share\\IManager' => $baseDir . '/lib/public/Share/IManager.php', 'OCP\\Share\\IProviderFactory' => $baseDir . '/lib/public/Share/IProviderFactory.php', 'OCP\\Share\\IShare' => $baseDir . '/lib/public/Share/IShare.php', 'OCP\\Share\\IShareHelper' => $baseDir . '/lib/public/Share/IShareHelper.php', 'OCP\\Share\\IShareProvider' => $baseDir . '/lib/public/Share/IShareProvider.php', 'OCP\\Share_Backend' => $baseDir . '/lib/public/Share_Backend.php', 'OCP\\Share_Backend_Collection' => $baseDir . '/lib/public/Share_Backend_Collection.php', 'OCP\\Share_Backend_File_Dependent' => $baseDir . '/lib/public/Share_Backend_File_Dependent.php', 'OCP\\SystemTag\\ISystemTag' => $baseDir . '/lib/public/SystemTag/ISystemTag.php', 'OCP\\SystemTag\\ISystemTagManager' => $baseDir . '/lib/public/SystemTag/ISystemTagManager.php', 'OCP\\SystemTag\\ISystemTagManagerFactory' => $baseDir . '/lib/public/SystemTag/ISystemTagManagerFactory.php', 'OCP\\SystemTag\\ISystemTagObjectMapper' => $baseDir . '/lib/public/SystemTag/ISystemTagObjectMapper.php', 'OCP\\SystemTag\\ManagerEvent' => $baseDir . '/lib/public/SystemTag/ManagerEvent.php', 'OCP\\SystemTag\\MapperEvent' => $baseDir . '/lib/public/SystemTag/MapperEvent.php', 'OCP\\SystemTag\\SystemTagsEntityEvent' => $baseDir . '/lib/public/SystemTag/SystemTagsEntityEvent.php', 'OCP\\SystemTag\\TagAlreadyExistsException' => $baseDir . '/lib/public/SystemTag/TagAlreadyExistsException.php', 'OCP\\SystemTag\\TagNotFoundException' => $baseDir . '/lib/public/SystemTag/TagNotFoundException.php', 'OCP\\Template' => $baseDir . '/lib/public/Template.php', 'OCP\\User' => $baseDir . '/lib/public/User.php', 'OCP\\UserInterface' => $baseDir . '/lib/public/UserInterface.php', 'OCP\\Util' => $baseDir . '/lib/public/Util.php', 'OCP\\WorkflowEngine\\ICheck' => $baseDir . '/lib/public/WorkflowEngine/ICheck.php', 'OCP\\WorkflowEngine\\IManager' => $baseDir . '/lib/public/WorkflowEngine/IManager.php', 'OCP\\WorkflowEngine\\IOperation' => $baseDir . '/lib/public/WorkflowEngine/IOperation.php', 'OC\\Accounts\\AccountManager' => $baseDir . '/lib/private/Accounts/AccountManager.php', 'OC\\Accounts\\Hooks' => $baseDir . '/lib/private/Accounts/Hooks.php', 'OC\\Activity\\Event' => $baseDir . '/lib/private/Activity/Event.php', 'OC\\Activity\\EventMerger' => $baseDir . '/lib/private/Activity/EventMerger.php', 'OC\\Activity\\LegacyFilter' => $baseDir . '/lib/private/Activity/LegacyFilter.php', 'OC\\Activity\\LegacySetting' => $baseDir . '/lib/private/Activity/LegacySetting.php', 'OC\\Activity\\Manager' => $baseDir . '/lib/private/Activity/Manager.php', 'OC\\AllConfig' => $baseDir . '/lib/private/AllConfig.php', 'OC\\AppConfig' => $baseDir . '/lib/private/AppConfig.php', 'OC\\AppFramework\\App' => $baseDir . '/lib/private/AppFramework/App.php', 'OC\\AppFramework\\Core\\API' => $baseDir . '/lib/private/AppFramework/Core/API.php', 'OC\\AppFramework\\DependencyInjection\\DIContainer' => $baseDir . '/lib/private/AppFramework/DependencyInjection/DIContainer.php', 'OC\\AppFramework\\Http' => $baseDir . '/lib/private/AppFramework/Http.php', 'OC\\AppFramework\\Http\\Dispatcher' => $baseDir . '/lib/private/AppFramework/Http/Dispatcher.php', 'OC\\AppFramework\\Http\\Output' => $baseDir . '/lib/private/AppFramework/Http/Output.php', 'OC\\AppFramework\\Http\\Request' => $baseDir . '/lib/private/AppFramework/Http/Request.php', 'OC\\AppFramework\\Middleware\\MiddlewareDispatcher' => $baseDir . '/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php', 'OC\\AppFramework\\Middleware\\OCSMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/OCSMiddleware.php', 'OC\\AppFramework\\Middleware\\Security\\BruteForceMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php', 'OC\\AppFramework\\Middleware\\Security\\CORSMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\AppNotEnabledException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\CrossSiteRequestForgeryException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotAdminException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotConfirmedException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotLoggedInException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\SecurityException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\StrictCookieMissingException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php', 'OC\\AppFramework\\Middleware\\Security\\RateLimitingMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php', 'OC\\AppFramework\\Middleware\\Security\\SecurityMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php', 'OC\\AppFramework\\Middleware\\SessionMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/SessionMiddleware.php', 'OC\\AppFramework\\OCS\\BaseResponse' => $baseDir . '/lib/private/AppFramework/OCS/BaseResponse.php', 'OC\\AppFramework\\OCS\\V1Response' => $baseDir . '/lib/private/AppFramework/OCS/V1Response.php', 'OC\\AppFramework\\OCS\\V2Response' => $baseDir . '/lib/private/AppFramework/OCS/V2Response.php', 'OC\\AppFramework\\Routing\\RouteActionHandler' => $baseDir . '/lib/private/AppFramework/Routing/RouteActionHandler.php', 'OC\\AppFramework\\Routing\\RouteConfig' => $baseDir . '/lib/private/AppFramework/Routing/RouteConfig.php', 'OC\\AppFramework\\Utility\\ControllerMethodReflector' => $baseDir . '/lib/private/AppFramework/Utility/ControllerMethodReflector.php', 'OC\\AppFramework\\Utility\\SimpleContainer' => $baseDir . '/lib/private/AppFramework/Utility/SimpleContainer.php', 'OC\\AppFramework\\Utility\\TimeFactory' => $baseDir . '/lib/private/AppFramework/Utility/TimeFactory.php', 'OC\\AppHelper' => $baseDir . '/lib/private/AppHelper.php', 'OC\\App\\AppManager' => $baseDir . '/lib/private/App/AppManager.php', 'OC\\App\\AppStore\\Bundles\\Bundle' => $baseDir . '/lib/private/App/AppStore/Bundles/Bundle.php', 'OC\\App\\AppStore\\Bundles\\BundleFetcher' => $baseDir . '/lib/private/App/AppStore/Bundles/BundleFetcher.php', 'OC\\App\\AppStore\\Bundles\\CoreBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/CoreBundle.php', 'OC\\App\\AppStore\\Bundles\\EducationBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/EducationBundle.php', 'OC\\App\\AppStore\\Bundles\\EnterpriseBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/EnterpriseBundle.php', 'OC\\App\\AppStore\\Bundles\\GroupwareBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/GroupwareBundle.php', 'OC\\App\\AppStore\\Bundles\\SocialSharingBundle' => $baseDir . '/lib/private/App/AppStore/Bundles/SocialSharingBundle.php', 'OC\\App\\AppStore\\Fetcher\\AppFetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/AppFetcher.php', 'OC\\App\\AppStore\\Fetcher\\CategoryFetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/CategoryFetcher.php', 'OC\\App\\AppStore\\Fetcher\\Fetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/Fetcher.php', 'OC\\App\\AppStore\\Version\\Version' => $baseDir . '/lib/private/App/AppStore/Version/Version.php', 'OC\\App\\AppStore\\Version\\VersionParser' => $baseDir . '/lib/private/App/AppStore/Version/VersionParser.php', 'OC\\App\\CodeChecker\\AbstractCheck' => $baseDir . '/lib/private/App/CodeChecker/AbstractCheck.php', 'OC\\App\\CodeChecker\\CodeChecker' => $baseDir . '/lib/private/App/CodeChecker/CodeChecker.php', 'OC\\App\\CodeChecker\\DatabaseSchemaChecker' => $baseDir . '/lib/private/App/CodeChecker/DatabaseSchemaChecker.php', 'OC\\App\\CodeChecker\\DeprecationCheck' => $baseDir . '/lib/private/App/CodeChecker/DeprecationCheck.php', 'OC\\App\\CodeChecker\\EmptyCheck' => $baseDir . '/lib/private/App/CodeChecker/EmptyCheck.php', 'OC\\App\\CodeChecker\\ICheck' => $baseDir . '/lib/private/App/CodeChecker/ICheck.php', 'OC\\App\\CodeChecker\\InfoChecker' => $baseDir . '/lib/private/App/CodeChecker/InfoChecker.php', 'OC\\App\\CodeChecker\\LanguageParseChecker' => $baseDir . '/lib/private/App/CodeChecker/LanguageParseChecker.php', 'OC\\App\\CodeChecker\\NodeVisitor' => $baseDir . '/lib/private/App/CodeChecker/NodeVisitor.php', 'OC\\App\\CodeChecker\\PrivateCheck' => $baseDir . '/lib/private/App/CodeChecker/PrivateCheck.php', 'OC\\App\\CodeChecker\\StrongComparisonCheck' => $baseDir . '/lib/private/App/CodeChecker/StrongComparisonCheck.php', 'OC\\App\\DependencyAnalyzer' => $baseDir . '/lib/private/App/DependencyAnalyzer.php', 'OC\\App\\InfoParser' => $baseDir . '/lib/private/App/InfoParser.php', 'OC\\App\\Platform' => $baseDir . '/lib/private/App/Platform.php', 'OC\\App\\PlatformRepository' => $baseDir . '/lib/private/App/PlatformRepository.php', 'OC\\Archive\\Archive' => $baseDir . '/lib/private/Archive/Archive.php', 'OC\\Archive\\TAR' => $baseDir . '/lib/private/Archive/TAR.php', 'OC\\Archive\\ZIP' => $baseDir . '/lib/private/Archive/ZIP.php', 'OC\\Authentication\\Exceptions\\InvalidTokenException' => $baseDir . '/lib/private/Authentication/Exceptions/InvalidTokenException.php', 'OC\\Authentication\\Exceptions\\LoginRequiredException' => $baseDir . '/lib/private/Authentication/Exceptions/LoginRequiredException.php', 'OC\\Authentication\\Exceptions\\PasswordLoginForbiddenException' => $baseDir . '/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php', 'OC\\Authentication\\Exceptions\\PasswordlessTokenException' => $baseDir . '/lib/private/Authentication/Exceptions/PasswordlessTokenException.php', 'OC\\Authentication\\Exceptions\\TwoFactorAuthRequiredException' => $baseDir . '/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php', 'OC\\Authentication\\Exceptions\\UserAlreadyLoggedInException' => $baseDir . '/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php', 'OC\\Authentication\\LoginCredentials\\Credentials' => $baseDir . '/lib/private/Authentication/LoginCredentials/Credentials.php', 'OC\\Authentication\\LoginCredentials\\Store' => $baseDir . '/lib/private/Authentication/LoginCredentials/Store.php', 'OC\\Authentication\\Token\\DefaultToken' => $baseDir . '/lib/private/Authentication/Token/DefaultToken.php', 'OC\\Authentication\\Token\\DefaultTokenCleanupJob' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenCleanupJob.php', 'OC\\Authentication\\Token\\DefaultTokenMapper' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenMapper.php', 'OC\\Authentication\\Token\\DefaultTokenProvider' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenProvider.php', 'OC\\Authentication\\Token\\IProvider' => $baseDir . '/lib/private/Authentication/Token/IProvider.php', 'OC\\Authentication\\Token\\IToken' => $baseDir . '/lib/private/Authentication/Token/IToken.php', 'OC\\Authentication\\TwoFactorAuth\\Manager' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/Manager.php', 'OC\\Avatar' => $baseDir . '/lib/private/Avatar.php', 'OC\\AvatarManager' => $baseDir . '/lib/private/AvatarManager.php', 'OC\\BackgroundJob\\Job' => $baseDir . '/lib/private/BackgroundJob/Job.php', 'OC\\BackgroundJob\\JobList' => $baseDir . '/lib/private/BackgroundJob/JobList.php', 'OC\\BackgroundJob\\Legacy\\QueuedJob' => $baseDir . '/lib/private/BackgroundJob/Legacy/QueuedJob.php', 'OC\\BackgroundJob\\Legacy\\RegularJob' => $baseDir . '/lib/private/BackgroundJob/Legacy/RegularJob.php', 'OC\\BackgroundJob\\QueuedJob' => $baseDir . '/lib/private/BackgroundJob/QueuedJob.php', 'OC\\BackgroundJob\\TimedJob' => $baseDir . '/lib/private/BackgroundJob/TimedJob.php', 'OC\\Cache\\CappedMemoryCache' => $baseDir . '/lib/private/Cache/CappedMemoryCache.php', 'OC\\Cache\\File' => $baseDir . '/lib/private/Cache/File.php', 'OC\\CapabilitiesManager' => $baseDir . '/lib/private/CapabilitiesManager.php', 'OC\\Command\\AsyncBus' => $baseDir . '/lib/private/Command/AsyncBus.php', 'OC\\Command\\CallableJob' => $baseDir . '/lib/private/Command/CallableJob.php', 'OC\\Command\\ClosureJob' => $baseDir . '/lib/private/Command/ClosureJob.php', 'OC\\Command\\CommandJob' => $baseDir . '/lib/private/Command/CommandJob.php', 'OC\\Command\\FileAccess' => $baseDir . '/lib/private/Command/FileAccess.php', 'OC\\Command\\QueueBus' => $baseDir . '/lib/private/Command/QueueBus.php', 'OC\\Comments\\Comment' => $baseDir . '/lib/private/Comments/Comment.php', 'OC\\Comments\\Manager' => $baseDir . '/lib/private/Comments/Manager.php', 'OC\\Comments\\ManagerFactory' => $baseDir . '/lib/private/Comments/ManagerFactory.php', 'OC\\Config' => $baseDir . '/lib/private/Config.php', 'OC\\Console\\Application' => $baseDir . '/lib/private/Console/Application.php', 'OC\\Console\\TimestampFormatter' => $baseDir . '/lib/private/Console/TimestampFormatter.php', 'OC\\ContactsManager' => $baseDir . '/lib/private/ContactsManager.php', 'OC\\Contacts\\ContactsMenu\\ActionFactory' => $baseDir . '/lib/private/Contacts/ContactsMenu/ActionFactory.php', 'OC\\Contacts\\ContactsMenu\\ActionProviderStore' => $baseDir . '/lib/private/Contacts/ContactsMenu/ActionProviderStore.php', 'OC\\Contacts\\ContactsMenu\\Actions\\LinkAction' => $baseDir . '/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php', 'OC\\Contacts\\ContactsMenu\\ContactsStore' => $baseDir . '/lib/private/Contacts/ContactsMenu/ContactsStore.php', 'OC\\Contacts\\ContactsMenu\\Entry' => $baseDir . '/lib/private/Contacts/ContactsMenu/Entry.php', 'OC\\Contacts\\ContactsMenu\\Manager' => $baseDir . '/lib/private/Contacts/ContactsMenu/Manager.php', 'OC\\Contacts\\ContactsMenu\\Providers\\EMailProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php', 'OC\\Core\\Application' => $baseDir . '/core/Application.php', 'OC\\Core\\Command\\App\\CheckCode' => $baseDir . '/core/Command/App/CheckCode.php', 'OC\\Core\\Command\\App\\Disable' => $baseDir . '/core/Command/App/Disable.php', 'OC\\Core\\Command\\App\\Enable' => $baseDir . '/core/Command/App/Enable.php', 'OC\\Core\\Command\\App\\GetPath' => $baseDir . '/core/Command/App/GetPath.php', 'OC\\Core\\Command\\App\\ListApps' => $baseDir . '/core/Command/App/ListApps.php', 'OC\\Core\\Command\\Background\\Ajax' => $baseDir . '/core/Command/Background/Ajax.php', 'OC\\Core\\Command\\Background\\Base' => $baseDir . '/core/Command/Background/Base.php', 'OC\\Core\\Command\\Background\\Cron' => $baseDir . '/core/Command/Background/Cron.php', 'OC\\Core\\Command\\Background\\WebCron' => $baseDir . '/core/Command/Background/WebCron.php', 'OC\\Core\\Command\\Base' => $baseDir . '/core/Command/Base.php', 'OC\\Core\\Command\\Check' => $baseDir . '/core/Command/Check.php', 'OC\\Core\\Command\\Config\\App\\Base' => $baseDir . '/core/Command/Config/App/Base.php', 'OC\\Core\\Command\\Config\\App\\DeleteConfig' => $baseDir . '/core/Command/Config/App/DeleteConfig.php', 'OC\\Core\\Command\\Config\\App\\GetConfig' => $baseDir . '/core/Command/Config/App/GetConfig.php', 'OC\\Core\\Command\\Config\\App\\SetConfig' => $baseDir . '/core/Command/Config/App/SetConfig.php', 'OC\\Core\\Command\\Config\\Import' => $baseDir . '/core/Command/Config/Import.php', 'OC\\Core\\Command\\Config\\ListConfigs' => $baseDir . '/core/Command/Config/ListConfigs.php', 'OC\\Core\\Command\\Config\\System\\Base' => $baseDir . '/core/Command/Config/System/Base.php', 'OC\\Core\\Command\\Config\\System\\DeleteConfig' => $baseDir . '/core/Command/Config/System/DeleteConfig.php', 'OC\\Core\\Command\\Config\\System\\GetConfig' => $baseDir . '/core/Command/Config/System/GetConfig.php', 'OC\\Core\\Command\\Config\\System\\SetConfig' => $baseDir . '/core/Command/Config/System/SetConfig.php', 'OC\\Core\\Command\\Db\\ConvertMysqlToMB4' => $baseDir . '/core/Command/Db/ConvertMysqlToMB4.php', 'OC\\Core\\Command\\Db\\ConvertType' => $baseDir . '/core/Command/Db/ConvertType.php', 'OC\\Core\\Command\\Db\\GenerateChangeScript' => $baseDir . '/core/Command/Db/GenerateChangeScript.php', 'OC\\Core\\Command\\Encryption\\ChangeKeyStorageRoot' => $baseDir . '/core/Command/Encryption/ChangeKeyStorageRoot.php', 'OC\\Core\\Command\\Encryption\\DecryptAll' => $baseDir . '/core/Command/Encryption/DecryptAll.php', 'OC\\Core\\Command\\Encryption\\Disable' => $baseDir . '/core/Command/Encryption/Disable.php', 'OC\\Core\\Command\\Encryption\\Enable' => $baseDir . '/core/Command/Encryption/Enable.php', 'OC\\Core\\Command\\Encryption\\EncryptAll' => $baseDir . '/core/Command/Encryption/EncryptAll.php', 'OC\\Core\\Command\\Encryption\\ListModules' => $baseDir . '/core/Command/Encryption/ListModules.php', 'OC\\Core\\Command\\Encryption\\SetDefaultModule' => $baseDir . '/core/Command/Encryption/SetDefaultModule.php', 'OC\\Core\\Command\\Encryption\\ShowKeyStorageRoot' => $baseDir . '/core/Command/Encryption/ShowKeyStorageRoot.php', 'OC\\Core\\Command\\Encryption\\Status' => $baseDir . '/core/Command/Encryption/Status.php', 'OC\\Core\\Command\\Group\\AddUser' => $baseDir . '/core/Command/Group/AddUser.php', 'OC\\Core\\Command\\Group\\ListCommand' => $baseDir . '/core/Command/Group/ListCommand.php', 'OC\\Core\\Command\\Group\\RemoveUser' => $baseDir . '/core/Command/Group/RemoveUser.php', 'OC\\Core\\Command\\Integrity\\CheckApp' => $baseDir . '/core/Command/Integrity/CheckApp.php', 'OC\\Core\\Command\\Integrity\\CheckCore' => $baseDir . '/core/Command/Integrity/CheckCore.php', 'OC\\Core\\Command\\Integrity\\SignApp' => $baseDir . '/core/Command/Integrity/SignApp.php', 'OC\\Core\\Command\\Integrity\\SignCore' => $baseDir . '/core/Command/Integrity/SignCore.php', 'OC\\Core\\Command\\InterruptedException' => $baseDir . '/core/Command/InterruptedException.php', 'OC\\Core\\Command\\L10n\\CreateJs' => $baseDir . '/core/Command/L10n/CreateJs.php', 'OC\\Core\\Command\\Log\\File' => $baseDir . '/core/Command/Log/File.php', 'OC\\Core\\Command\\Log\\Manage' => $baseDir . '/core/Command/Log/Manage.php', 'OC\\Core\\Command\\Maintenance\\DataFingerprint' => $baseDir . '/core/Command/Maintenance/DataFingerprint.php', 'OC\\Core\\Command\\Maintenance\\Install' => $baseDir . '/core/Command/Maintenance/Install.php', 'OC\\Core\\Command\\Maintenance\\Mimetype\\UpdateDB' => $baseDir . '/core/Command/Maintenance/Mimetype/UpdateDB.php', 'OC\\Core\\Command\\Maintenance\\Mimetype\\UpdateJS' => $baseDir . '/core/Command/Maintenance/Mimetype/UpdateJS.php', 'OC\\Core\\Command\\Maintenance\\Mode' => $baseDir . '/core/Command/Maintenance/Mode.php', 'OC\\Core\\Command\\Maintenance\\Repair' => $baseDir . '/core/Command/Maintenance/Repair.php', 'OC\\Core\\Command\\Maintenance\\UpdateHtaccess' => $baseDir . '/core/Command/Maintenance/UpdateHtaccess.php', 'OC\\Core\\Command\\Security\\ImportCertificate' => $baseDir . '/core/Command/Security/ImportCertificate.php', 'OC\\Core\\Command\\Security\\ListCertificates' => $baseDir . '/core/Command/Security/ListCertificates.php', 'OC\\Core\\Command\\Security\\RemoveCertificate' => $baseDir . '/core/Command/Security/RemoveCertificate.php', 'OC\\Core\\Command\\Status' => $baseDir . '/core/Command/Status.php', 'OC\\Core\\Command\\TwoFactorAuth\\Base' => $baseDir . '/core/Command/TwoFactorAuth/Base.php', 'OC\\Core\\Command\\TwoFactorAuth\\Disable' => $baseDir . '/core/Command/TwoFactorAuth/Disable.php', 'OC\\Core\\Command\\TwoFactorAuth\\Enable' => $baseDir . '/core/Command/TwoFactorAuth/Enable.php', 'OC\\Core\\Command\\Upgrade' => $baseDir . '/core/Command/Upgrade.php', 'OC\\Core\\Command\\User\\Add' => $baseDir . '/core/Command/User/Add.php', 'OC\\Core\\Command\\User\\Delete' => $baseDir . '/core/Command/User/Delete.php', 'OC\\Core\\Command\\User\\Disable' => $baseDir . '/core/Command/User/Disable.php', 'OC\\Core\\Command\\User\\Enable' => $baseDir . '/core/Command/User/Enable.php', 'OC\\Core\\Command\\User\\Info' => $baseDir . '/core/Command/User/Info.php', 'OC\\Core\\Command\\User\\LastSeen' => $baseDir . '/core/Command/User/LastSeen.php', 'OC\\Core\\Command\\User\\ListCommand' => $baseDir . '/core/Command/User/ListCommand.php', 'OC\\Core\\Command\\User\\Report' => $baseDir . '/core/Command/User/Report.php', 'OC\\Core\\Command\\User\\ResetPassword' => $baseDir . '/core/Command/User/ResetPassword.php', 'OC\\Core\\Command\\User\\Setting' => $baseDir . '/core/Command/User/Setting.php', 'OC\\Core\\Controller\\AvatarController' => $baseDir . '/core/Controller/AvatarController.php', 'OC\\Core\\Controller\\ClientFlowLoginController' => $baseDir . '/core/Controller/ClientFlowLoginController.php', 'OC\\Core\\Controller\\ContactsMenuController' => $baseDir . '/core/Controller/ContactsMenuController.php', 'OC\\Core\\Controller\\CssController' => $baseDir . '/core/Controller/CssController.php', 'OC\\Core\\Controller\\JsController' => $baseDir . '/core/Controller/JsController.php', 'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\OCJSController' => $baseDir . '/core/Controller/OCJSController.php', 'OC\\Core\\Controller\\OCSController' => $baseDir . '/core/Controller/OCSController.php', 'OC\\Core\\Controller\\PreviewController' => $baseDir . '/core/Controller/PreviewController.php', 'OC\\Core\\Controller\\SetupController' => $baseDir . '/core/Controller/SetupController.php', 'OC\\Core\\Controller\\TwoFactorChallengeController' => $baseDir . '/core/Controller/TwoFactorChallengeController.php', 'OC\\Core\\Controller\\UserController' => $baseDir . '/core/Controller/UserController.php', 'OC\\Core\\Middleware\\TwoFactorMiddleware' => $baseDir . '/core/Middleware/TwoFactorMiddleware.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', 'OC\\DB\\AdapterMySQL' => $baseDir . '/lib/private/DB/AdapterMySQL.php', 'OC\\DB\\AdapterOCI8' => $baseDir . '/lib/private/DB/AdapterOCI8.php', 'OC\\DB\\AdapterPgSql' => $baseDir . '/lib/private/DB/AdapterPgSql.php', 'OC\\DB\\AdapterSqlite' => $baseDir . '/lib/private/DB/AdapterSqlite.php', 'OC\\DB\\Connection' => $baseDir . '/lib/private/DB/Connection.php', 'OC\\DB\\ConnectionFactory' => $baseDir . '/lib/private/DB/ConnectionFactory.php', 'OC\\DB\\MDB2SchemaManager' => $baseDir . '/lib/private/DB/MDB2SchemaManager.php', 'OC\\DB\\MDB2SchemaReader' => $baseDir . '/lib/private/DB/MDB2SchemaReader.php', 'OC\\DB\\MDB2SchemaWriter' => $baseDir . '/lib/private/DB/MDB2SchemaWriter.php', 'OC\\DB\\MigrationException' => $baseDir . '/lib/private/DB/MigrationException.php', 'OC\\DB\\Migrator' => $baseDir . '/lib/private/DB/Migrator.php', 'OC\\DB\\MySQLMigrator' => $baseDir . '/lib/private/DB/MySQLMigrator.php', 'OC\\DB\\MySqlTools' => $baseDir . '/lib/private/DB/MySqlTools.php', 'OC\\DB\\NoCheckMigrator' => $baseDir . '/lib/private/DB/NoCheckMigrator.php', 'OC\\DB\\OCSqlitePlatform' => $baseDir . '/lib/private/DB/OCSqlitePlatform.php', 'OC\\DB\\OracleConnection' => $baseDir . '/lib/private/DB/OracleConnection.php', 'OC\\DB\\OracleMigrator' => $baseDir . '/lib/private/DB/OracleMigrator.php', 'OC\\DB\\PgSqlTools' => $baseDir . '/lib/private/DB/PgSqlTools.php', 'OC\\DB\\PostgreSqlMigrator' => $baseDir . '/lib/private/DB/PostgreSqlMigrator.php', 'OC\\DB\\QueryBuilder\\CompositeExpression' => $baseDir . '/lib/private/DB/QueryBuilder/CompositeExpression.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\ExpressionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\MySqlExpressionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\OCIExpressionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\PgSqlExpressionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\SqliteExpressionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\FunctionBuilder\\FunctionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php', 'OC\\DB\\QueryBuilder\\FunctionBuilder\\OCIFunctionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php', 'OC\\DB\\QueryBuilder\\FunctionBuilder\\PgSqlFunctionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php', 'OC\\DB\\QueryBuilder\\FunctionBuilder\\SqliteFunctionBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php', 'OC\\DB\\QueryBuilder\\Literal' => $baseDir . '/lib/private/DB/QueryBuilder/Literal.php', 'OC\\DB\\QueryBuilder\\Parameter' => $baseDir . '/lib/private/DB/QueryBuilder/Parameter.php', 'OC\\DB\\QueryBuilder\\QueryBuilder' => $baseDir . '/lib/private/DB/QueryBuilder/QueryBuilder.php', 'OC\\DB\\QueryBuilder\\QueryFunction' => $baseDir . '/lib/private/DB/QueryBuilder/QueryFunction.php', 'OC\\DB\\QueryBuilder\\QuoteHelper' => $baseDir . '/lib/private/DB/QueryBuilder/QuoteHelper.php', 'OC\\DB\\SQLiteMigrator' => $baseDir . '/lib/private/DB/SQLiteMigrator.php', 'OC\\DB\\SQLiteSessionInit' => $baseDir . '/lib/private/DB/SQLiteSessionInit.php', 'OC\\DatabaseException' => $baseDir . '/lib/private/DatabaseException.php', 'OC\\DatabaseSetupException' => $baseDir . '/lib/private/DatabaseSetupException.php', 'OC\\DateTimeFormatter' => $baseDir . '/lib/private/DateTimeFormatter.php', 'OC\\DateTimeZone' => $baseDir . '/lib/private/DateTimeZone.php', 'OC\\Diagnostics\\Event' => $baseDir . '/lib/private/Diagnostics/Event.php', 'OC\\Diagnostics\\EventLogger' => $baseDir . '/lib/private/Diagnostics/EventLogger.php', 'OC\\Diagnostics\\Query' => $baseDir . '/lib/private/Diagnostics/Query.php', 'OC\\Diagnostics\\QueryLogger' => $baseDir . '/lib/private/Diagnostics/QueryLogger.php', 'OC\\Encryption\\DecryptAll' => $baseDir . '/lib/private/Encryption/DecryptAll.php', 'OC\\Encryption\\EncryptionWrapper' => $baseDir . '/lib/private/Encryption/EncryptionWrapper.php', 'OC\\Encryption\\Exceptions\\DecryptionFailedException' => $baseDir . '/lib/private/Encryption/Exceptions/DecryptionFailedException.php', 'OC\\Encryption\\Exceptions\\EmptyEncryptionDataException' => $baseDir . '/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php', 'OC\\Encryption\\Exceptions\\EncryptionFailedException' => $baseDir . '/lib/private/Encryption/Exceptions/EncryptionFailedException.php', 'OC\\Encryption\\Exceptions\\EncryptionHeaderKeyExistsException' => $baseDir . '/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php', 'OC\\Encryption\\Exceptions\\EncryptionHeaderToLargeException' => $baseDir . '/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php', 'OC\\Encryption\\Exceptions\\ModuleAlreadyExistsException' => $baseDir . '/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php', 'OC\\Encryption\\Exceptions\\ModuleDoesNotExistsException' => $baseDir . '/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php', 'OC\\Encryption\\Exceptions\\UnknownCipherException' => $baseDir . '/lib/private/Encryption/Exceptions/UnknownCipherException.php', 'OC\\Encryption\\File' => $baseDir . '/lib/private/Encryption/File.php', 'OC\\Encryption\\HookManager' => $baseDir . '/lib/private/Encryption/HookManager.php', 'OC\\Encryption\\Keys\\Storage' => $baseDir . '/lib/private/Encryption/Keys/Storage.php', 'OC\\Encryption\\Manager' => $baseDir . '/lib/private/Encryption/Manager.php', 'OC\\Encryption\\Update' => $baseDir . '/lib/private/Encryption/Update.php', 'OC\\Encryption\\Util' => $baseDir . '/lib/private/Encryption/Util.php', 'OC\\Federation\\CloudId' => $baseDir . '/lib/private/Federation/CloudId.php', 'OC\\Federation\\CloudIdManager' => $baseDir . '/lib/private/Federation/CloudIdManager.php', 'OC\\Files\\AppData\\AppData' => $baseDir . '/lib/private/Files/AppData/AppData.php', 'OC\\Files\\AppData\\Factory' => $baseDir . '/lib/private/Files/AppData/Factory.php', 'OC\\Files\\Cache\\Cache' => $baseDir . '/lib/private/Files/Cache/Cache.php', 'OC\\Files\\Cache\\CacheEntry' => $baseDir . '/lib/private/Files/Cache/CacheEntry.php', 'OC\\Files\\Cache\\FailedCache' => $baseDir . '/lib/private/Files/Cache/FailedCache.php', 'OC\\Files\\Cache\\HomeCache' => $baseDir . '/lib/private/Files/Cache/HomeCache.php', 'OC\\Files\\Cache\\HomePropagator' => $baseDir . '/lib/private/Files/Cache/HomePropagator.php', 'OC\\Files\\Cache\\MoveFromCacheTrait' => $baseDir . '/lib/private/Files/Cache/MoveFromCacheTrait.php', 'OC\\Files\\Cache\\Propagator' => $baseDir . '/lib/private/Files/Cache/Propagator.php', 'OC\\Files\\Cache\\QuerySearchHelper' => $baseDir . '/lib/private/Files/Cache/QuerySearchHelper.php', 'OC\\Files\\Cache\\Scanner' => $baseDir . '/lib/private/Files/Cache/Scanner.php', 'OC\\Files\\Cache\\Storage' => $baseDir . '/lib/private/Files/Cache/Storage.php', 'OC\\Files\\Cache\\StorageGlobal' => $baseDir . '/lib/private/Files/Cache/StorageGlobal.php', 'OC\\Files\\Cache\\Updater' => $baseDir . '/lib/private/Files/Cache/Updater.php', 'OC\\Files\\Cache\\Watcher' => $baseDir . '/lib/private/Files/Cache/Watcher.php', 'OC\\Files\\Cache\\Wrapper\\CacheJail' => $baseDir . '/lib/private/Files/Cache/Wrapper/CacheJail.php', 'OC\\Files\\Cache\\Wrapper\\CachePermissionsMask' => $baseDir . '/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php', 'OC\\Files\\Cache\\Wrapper\\CacheWrapper' => $baseDir . '/lib/private/Files/Cache/Wrapper/CacheWrapper.php', 'OC\\Files\\Cache\\Wrapper\\JailPropagator' => $baseDir . '/lib/private/Files/Cache/Wrapper/JailPropagator.php', 'OC\\Files\\Config\\CachedMountInfo' => $baseDir . '/lib/private/Files/Config/CachedMountInfo.php', 'OC\\Files\\Config\\LazyStorageMountInfo' => $baseDir . '/lib/private/Files/Config/LazyStorageMountInfo.php', 'OC\\Files\\Config\\MountProviderCollection' => $baseDir . '/lib/private/Files/Config/MountProviderCollection.php', 'OC\\Files\\Config\\UserMountCache' => $baseDir . '/lib/private/Files/Config/UserMountCache.php', 'OC\\Files\\Config\\UserMountCacheListener' => $baseDir . '/lib/private/Files/Config/UserMountCacheListener.php', 'OC\\Files\\FileInfo' => $baseDir . '/lib/private/Files/FileInfo.php', 'OC\\Files\\Filesystem' => $baseDir . '/lib/private/Files/Filesystem.php', 'OC\\Files\\Mount\\CacheMountProvider' => $baseDir . '/lib/private/Files/Mount/CacheMountProvider.php', 'OC\\Files\\Mount\\LocalHomeMountProvider' => $baseDir . '/lib/private/Files/Mount/LocalHomeMountProvider.php', 'OC\\Files\\Mount\\Manager' => $baseDir . '/lib/private/Files/Mount/Manager.php', 'OC\\Files\\Mount\\MountPoint' => $baseDir . '/lib/private/Files/Mount/MountPoint.php', 'OC\\Files\\Mount\\MoveableMount' => $baseDir . '/lib/private/Files/Mount/MoveableMount.php', 'OC\\Files\\Mount\\ObjectHomeMountProvider' => $baseDir . '/lib/private/Files/Mount/ObjectHomeMountProvider.php', 'OC\\Files\\Node\\File' => $baseDir . '/lib/private/Files/Node/File.php', 'OC\\Files\\Node\\Folder' => $baseDir . '/lib/private/Files/Node/Folder.php', 'OC\\Files\\Node\\HookConnector' => $baseDir . '/lib/private/Files/Node/HookConnector.php', 'OC\\Files\\Node\\LazyRoot' => $baseDir . '/lib/private/Files/Node/LazyRoot.php', 'OC\\Files\\Node\\Node' => $baseDir . '/lib/private/Files/Node/Node.php', 'OC\\Files\\Node\\NonExistingFile' => $baseDir . '/lib/private/Files/Node/NonExistingFile.php', 'OC\\Files\\Node\\NonExistingFolder' => $baseDir . '/lib/private/Files/Node/NonExistingFolder.php', 'OC\\Files\\Node\\Root' => $baseDir . '/lib/private/Files/Node/Root.php', 'OC\\Files\\Notify\\Change' => $baseDir . '/lib/private/Files/Notify/Change.php', 'OC\\Files\\Notify\\RenameChange' => $baseDir . '/lib/private/Files/Notify/RenameChange.php', 'OC\\Files\\ObjectStore\\HomeObjectStoreStorage' => $baseDir . '/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\Mapper' => $baseDir . '/lib/private/Files/ObjectStore/Mapper.php', 'OC\\Files\\ObjectStore\\NoopScanner' => $baseDir . '/lib/private/Files/ObjectStore/NoopScanner.php', 'OC\\Files\\ObjectStore\\ObjectStoreStorage' => $baseDir . '/lib/private/Files/ObjectStore/ObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\S3' => $baseDir . '/lib/private/Files/ObjectStore/S3.php', 'OC\\Files\\ObjectStore\\S3ConnectionTrait' => $baseDir . '/lib/private/Files/ObjectStore/S3ConnectionTrait.php', 'OC\\Files\\ObjectStore\\StorageObjectStore' => $baseDir . '/lib/private/Files/ObjectStore/StorageObjectStore.php', 'OC\\Files\\ObjectStore\\Swift' => $baseDir . '/lib/private/Files/ObjectStore/Swift.php', 'OC\\Files\\Search\\SearchBinaryOperator' => $baseDir . '/lib/private/Files/Search/SearchBinaryOperator.php', 'OC\\Files\\Search\\SearchComparison' => $baseDir . '/lib/private/Files/Search/SearchComparison.php', 'OC\\Files\\Search\\SearchOrder' => $baseDir . '/lib/private/Files/Search/SearchOrder.php', 'OC\\Files\\Search\\SearchQuery' => $baseDir . '/lib/private/Files/Search/SearchQuery.php', 'OC\\Files\\SimpleFS\\SimpleFile' => $baseDir . '/lib/private/Files/SimpleFS/SimpleFile.php', 'OC\\Files\\SimpleFS\\SimpleFolder' => $baseDir . '/lib/private/Files/SimpleFS/SimpleFolder.php', 'OC\\Files\\Storage\\Common' => $baseDir . '/lib/private/Files/Storage/Common.php', 'OC\\Files\\Storage\\CommonTest' => $baseDir . '/lib/private/Files/Storage/CommonTest.php', 'OC\\Files\\Storage\\DAV' => $baseDir . '/lib/private/Files/Storage/DAV.php', 'OC\\Files\\Storage\\FailedStorage' => $baseDir . '/lib/private/Files/Storage/FailedStorage.php', 'OC\\Files\\Storage\\Flysystem' => $baseDir . '/lib/private/Files/Storage/Flysystem.php', 'OC\\Files\\Storage\\Home' => $baseDir . '/lib/private/Files/Storage/Home.php', 'OC\\Files\\Storage\\Local' => $baseDir . '/lib/private/Files/Storage/Local.php', 'OC\\Files\\Storage\\LocalTempFileTrait' => $baseDir . '/lib/private/Files/Storage/LocalTempFileTrait.php', 'OC\\Files\\Storage\\PolyFill\\CopyDirectory' => $baseDir . '/lib/private/Files/Storage/PolyFill/CopyDirectory.php', 'OC\\Files\\Storage\\Storage' => $baseDir . '/lib/private/Files/Storage/Storage.php', 'OC\\Files\\Storage\\StorageFactory' => $baseDir . '/lib/private/Files/Storage/StorageFactory.php', 'OC\\Files\\Storage\\Temporary' => $baseDir . '/lib/private/Files/Storage/Temporary.php', 'OC\\Files\\Storage\\Wrapper\\Availability' => $baseDir . '/lib/private/Files/Storage/Wrapper/Availability.php', 'OC\\Files\\Storage\\Wrapper\\Encoding' => $baseDir . '/lib/private/Files/Storage/Wrapper/Encoding.php', 'OC\\Files\\Storage\\Wrapper\\Encryption' => $baseDir . '/lib/private/Files/Storage/Wrapper/Encryption.php', 'OC\\Files\\Storage\\Wrapper\\Jail' => $baseDir . '/lib/private/Files/Storage/Wrapper/Jail.php', 'OC\\Files\\Storage\\Wrapper\\PermissionsMask' => $baseDir . '/lib/private/Files/Storage/Wrapper/PermissionsMask.php', 'OC\\Files\\Storage\\Wrapper\\Quota' => $baseDir . '/lib/private/Files/Storage/Wrapper/Quota.php', 'OC\\Files\\Storage\\Wrapper\\Wrapper' => $baseDir . '/lib/private/Files/Storage/Wrapper/Wrapper.php', 'OC\\Files\\Stream\\Encryption' => $baseDir . '/lib/private/Files/Stream/Encryption.php', 'OC\\Files\\Stream\\Quota' => $baseDir . '/lib/private/Files/Stream/Quota.php', 'OC\\Files\\Type\\Detection' => $baseDir . '/lib/private/Files/Type/Detection.php', 'OC\\Files\\Type\\Loader' => $baseDir . '/lib/private/Files/Type/Loader.php', 'OC\\Files\\Type\\TemplateManager' => $baseDir . '/lib/private/Files/Type/TemplateManager.php', 'OC\\Files\\Utils\\Scanner' => $baseDir . '/lib/private/Files/Utils/Scanner.php', 'OC\\Files\\View' => $baseDir . '/lib/private/Files/View.php', 'OC\\ForbiddenException' => $baseDir . '/lib/private/ForbiddenException.php', 'OC\\GlobalScale\\Config' => $baseDir . '/lib/private/GlobalScale/Config.php', 'OC\\Group\\Backend' => $baseDir . '/lib/private/Group/Backend.php', 'OC\\Group\\Database' => $baseDir . '/lib/private/Group/Database.php', 'OC\\Group\\Group' => $baseDir . '/lib/private/Group/Group.php', 'OC\\Group\\Manager' => $baseDir . '/lib/private/Group/Manager.php', 'OC\\Group\\MetaData' => $baseDir . '/lib/private/Group/MetaData.php', 'OC\\HTTPHelper' => $baseDir . '/lib/private/HTTPHelper.php', 'OC\\HintException' => $baseDir . '/lib/private/HintException.php', 'OC\\Hooks\\BasicEmitter' => $baseDir . '/lib/private/Hooks/BasicEmitter.php', 'OC\\Hooks\\Emitter' => $baseDir . '/lib/private/Hooks/Emitter.php', 'OC\\Hooks\\EmitterTrait' => $baseDir . '/lib/private/Hooks/EmitterTrait.php', 'OC\\Hooks\\ForwardingEmitter' => $baseDir . '/lib/private/Hooks/ForwardingEmitter.php', 'OC\\Hooks\\LegacyEmitter' => $baseDir . '/lib/private/Hooks/LegacyEmitter.php', 'OC\\Hooks\\PublicEmitter' => $baseDir . '/lib/private/Hooks/PublicEmitter.php', 'OC\\Http\\Client\\Client' => $baseDir . '/lib/private/Http/Client/Client.php', 'OC\\Http\\Client\\ClientService' => $baseDir . '/lib/private/Http/Client/ClientService.php', 'OC\\Http\\Client\\Response' => $baseDir . '/lib/private/Http/Client/Response.php', 'OC\\Installer' => $baseDir . '/lib/private/Installer.php', 'OC\\IntegrityCheck\\Checker' => $baseDir . '/lib/private/IntegrityCheck/Checker.php', 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException' => $baseDir . '/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php', 'OC\\IntegrityCheck\\Helpers\\AppLocator' => $baseDir . '/lib/private/IntegrityCheck/Helpers/AppLocator.php', 'OC\\IntegrityCheck\\Helpers\\EnvironmentHelper' => $baseDir . '/lib/private/IntegrityCheck/Helpers/EnvironmentHelper.php', 'OC\\IntegrityCheck\\Helpers\\FileAccessHelper' => $baseDir . '/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php', 'OC\\IntegrityCheck\\Iterator\\ExcludeFileByNameFilterIterator' => $baseDir . '/lib/private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php', 'OC\\IntegrityCheck\\Iterator\\ExcludeFoldersByPathFilterIterator' => $baseDir . '/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php', 'OC\\L10N\\Factory' => $baseDir . '/lib/private/L10N/Factory.php', 'OC\\L10N\\L10N' => $baseDir . '/lib/private/L10N/L10N.php', 'OC\\L10N\\LanguageNotFoundException' => $baseDir . '/lib/private/L10N/LanguageNotFoundException.php', 'OC\\LargeFileHelper' => $baseDir . '/lib/private/LargeFileHelper.php', 'OC\\Lock\\AbstractLockingProvider' => $baseDir . '/lib/private/Lock/AbstractLockingProvider.php', 'OC\\Lock\\DBLockingProvider' => $baseDir . '/lib/private/Lock/DBLockingProvider.php', 'OC\\Lock\\MemcacheLockingProvider' => $baseDir . '/lib/private/Lock/MemcacheLockingProvider.php', 'OC\\Lock\\NoopLockingProvider' => $baseDir . '/lib/private/Lock/NoopLockingProvider.php', 'OC\\Lockdown\\Filesystem\\NullCache' => $baseDir . '/lib/private/Lockdown/Filesystem/NullCache.php', 'OC\\Lockdown\\Filesystem\\NullStorage' => $baseDir . '/lib/private/Lockdown/Filesystem/NullStorage.php', 'OC\\Lockdown\\LockdownManager' => $baseDir . '/lib/private/Lockdown/LockdownManager.php', 'OC\\Log' => $baseDir . '/lib/private/Log.php', 'OC\\Log\\ErrorHandler' => $baseDir . '/lib/private/Log/ErrorHandler.php', 'OC\\Log\\Errorlog' => $baseDir . '/lib/private/Log/Errorlog.php', 'OC\\Log\\File' => $baseDir . '/lib/private/Log/File.php', 'OC\\Log\\Rotate' => $baseDir . '/lib/private/Log/Rotate.php', 'OC\\Log\\Syslog' => $baseDir . '/lib/private/Log/Syslog.php', 'OC\\Mail\\EMailTemplate' => $baseDir . '/lib/private/Mail/EMailTemplate.php', 'OC\\Mail\\Mailer' => $baseDir . '/lib/private/Mail/Mailer.php', 'OC\\Mail\\Message' => $baseDir . '/lib/private/Mail/Message.php', 'OC\\Memcache\\APCu' => $baseDir . '/lib/private/Memcache/APCu.php', 'OC\\Memcache\\ArrayCache' => $baseDir . '/lib/private/Memcache/ArrayCache.php', 'OC\\Memcache\\CADTrait' => $baseDir . '/lib/private/Memcache/CADTrait.php', 'OC\\Memcache\\CASTrait' => $baseDir . '/lib/private/Memcache/CASTrait.php', 'OC\\Memcache\\Cache' => $baseDir . '/lib/private/Memcache/Cache.php', 'OC\\Memcache\\Factory' => $baseDir . '/lib/private/Memcache/Factory.php', 'OC\\Memcache\\Memcached' => $baseDir . '/lib/private/Memcache/Memcached.php', 'OC\\Memcache\\NullCache' => $baseDir . '/lib/private/Memcache/NullCache.php', 'OC\\Memcache\\Redis' => $baseDir . '/lib/private/Memcache/Redis.php', 'OC\\Memcache\\XCache' => $baseDir . '/lib/private/Memcache/XCache.php', 'OC\\Migration\\BackgroundRepair' => $baseDir . '/lib/private/Migration/BackgroundRepair.php', 'OC\\Migration\\ConsoleOutput' => $baseDir . '/lib/private/Migration/ConsoleOutput.php', 'OC\\NaturalSort' => $baseDir . '/lib/private/NaturalSort.php', 'OC\\NaturalSort_DefaultCollator' => $baseDir . '/lib/private/NaturalSort_DefaultCollator.php', 'OC\\NavigationManager' => $baseDir . '/lib/private/NavigationManager.php', 'OC\\NeedsUpdateException' => $baseDir . '/lib/private/NeedsUpdateException.php', 'OC\\NotSquareException' => $baseDir . '/lib/private/NotSquareException.php', 'OC\\Notification\\Action' => $baseDir . '/lib/private/Notification/Action.php', 'OC\\Notification\\Manager' => $baseDir . '/lib/private/Notification/Manager.php', 'OC\\Notification\\Notification' => $baseDir . '/lib/private/Notification/Notification.php', 'OC\\OCS\\CoreCapabilities' => $baseDir . '/lib/private/OCS/CoreCapabilities.php', 'OC\\OCS\\DiscoveryService' => $baseDir . '/lib/private/OCS/DiscoveryService.php', 'OC\\OCS\\Exception' => $baseDir . '/lib/private/OCS/Exception.php', 'OC\\OCS\\PrivateData' => $baseDir . '/lib/private/OCS/PrivateData.php', 'OC\\OCS\\Provider' => $baseDir . '/lib/private/OCS/Provider.php', 'OC\\OCS\\Result' => $baseDir . '/lib/private/OCS/Result.php', 'OC\\PreviewManager' => $baseDir . '/lib/private/PreviewManager.php', 'OC\\PreviewNotAvailableException' => $baseDir . '/lib/private/PreviewNotAvailableException.php', 'OC\\Preview\\BMP' => $baseDir . '/lib/private/Preview/BMP.php', 'OC\\Preview\\Bitmap' => $baseDir . '/lib/private/Preview/Bitmap.php', 'OC\\Preview\\Font' => $baseDir . '/lib/private/Preview/Font.php', 'OC\\Preview\\GIF' => $baseDir . '/lib/private/Preview/GIF.php', 'OC\\Preview\\Generator' => $baseDir . '/lib/private/Preview/Generator.php', 'OC\\Preview\\GeneratorHelper' => $baseDir . '/lib/private/Preview/GeneratorHelper.php', 'OC\\Preview\\Illustrator' => $baseDir . '/lib/private/Preview/Illustrator.php', 'OC\\Preview\\Image' => $baseDir . '/lib/private/Preview/Image.php', 'OC\\Preview\\JPEG' => $baseDir . '/lib/private/Preview/JPEG.php', 'OC\\Preview\\MP3' => $baseDir . '/lib/private/Preview/MP3.php', 'OC\\Preview\\MSOffice2003' => $baseDir . '/lib/private/Preview/MSOffice2003.php', 'OC\\Preview\\MSOffice2007' => $baseDir . '/lib/private/Preview/MSOffice2007.php', 'OC\\Preview\\MSOfficeDoc' => $baseDir . '/lib/private/Preview/MSOfficeDoc.php', 'OC\\Preview\\MarkDown' => $baseDir . '/lib/private/Preview/MarkDown.php', 'OC\\Preview\\Movie' => $baseDir . '/lib/private/Preview/Movie.php', 'OC\\Preview\\Office' => $baseDir . '/lib/private/Preview/Office.php', 'OC\\Preview\\OpenDocument' => $baseDir . '/lib/private/Preview/OpenDocument.php', 'OC\\Preview\\PDF' => $baseDir . '/lib/private/Preview/PDF.php', 'OC\\Preview\\PNG' => $baseDir . '/lib/private/Preview/PNG.php', 'OC\\Preview\\Photoshop' => $baseDir . '/lib/private/Preview/Photoshop.php', 'OC\\Preview\\Postscript' => $baseDir . '/lib/private/Preview/Postscript.php', 'OC\\Preview\\Provider' => $baseDir . '/lib/private/Preview/Provider.php', 'OC\\Preview\\SVG' => $baseDir . '/lib/private/Preview/SVG.php', 'OC\\Preview\\StarOffice' => $baseDir . '/lib/private/Preview/StarOffice.php', 'OC\\Preview\\TIFF' => $baseDir . '/lib/private/Preview/TIFF.php', 'OC\\Preview\\TXT' => $baseDir . '/lib/private/Preview/TXT.php', 'OC\\Preview\\Watcher' => $baseDir . '/lib/private/Preview/Watcher.php', 'OC\\Preview\\WatcherConnector' => $baseDir . '/lib/private/Preview/WatcherConnector.php', 'OC\\Preview\\XBitmap' => $baseDir . '/lib/private/Preview/XBitmap.php', 'OC\\RedisFactory' => $baseDir . '/lib/private/RedisFactory.php', 'OC\\Repair' => $baseDir . '/lib/private/Repair.php', 'OC\\RepairException' => $baseDir . '/lib/private/RepairException.php', 'OC\\Repair\\CleanTags' => $baseDir . '/lib/private/Repair/CleanTags.php', 'OC\\Repair\\Collation' => $baseDir . '/lib/private/Repair/Collation.php', 'OC\\Repair\\MoveUpdaterStepFile' => $baseDir . '/lib/private/Repair/MoveUpdaterStepFile.php', 'OC\\Repair\\NC11\\CleanPreviews' => $baseDir . '/lib/private/Repair/NC11/CleanPreviews.php', 'OC\\Repair\\NC11\\CleanPreviewsBackgroundJob' => $baseDir . '/lib/private/Repair/NC11/CleanPreviewsBackgroundJob.php', 'OC\\Repair\\NC11\\FixMountStorages' => $baseDir . '/lib/private/Repair/NC11/FixMountStorages.php', 'OC\\Repair\\NC11\\MoveAvatars' => $baseDir . '/lib/private/Repair/NC11/MoveAvatars.php', 'OC\\Repair\\NC11\\MoveAvatarsBackgroundJob' => $baseDir . '/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php', 'OC\\Repair\\NC12\\InstallCoreBundle' => $baseDir . '/lib/private/Repair/NC12/InstallCoreBundle.php', 'OC\\Repair\\NC12\\RepairIdentityProofKeyFolders' => $baseDir . '/lib/private/Repair/NC12/RepairIdentityProofKeyFolders.php', 'OC\\Repair\\NC12\\UpdateLanguageCodes' => $baseDir . '/lib/private/Repair/NC12/UpdateLanguageCodes.php', 'OC\\Repair\\NC13\\RepairInvalidPaths' => $baseDir . '/lib/private/Repair/NC13/RepairInvalidPaths.php', 'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php', 'OC\\Repair\\Owncloud\\DropAccountTermsTable' => $baseDir . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php', 'OC\\Repair\\Owncloud\\SaveAccountsTableData' => $baseDir . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php', 'OC\\Repair\\RemoveRootShares' => $baseDir . '/lib/private/Repair/RemoveRootShares.php', 'OC\\Repair\\RepairInvalidShares' => $baseDir . '/lib/private/Repair/RepairInvalidShares.php', 'OC\\Repair\\RepairMimeTypes' => $baseDir . '/lib/private/Repair/RepairMimeTypes.php', 'OC\\Repair\\SqliteAutoincrement' => $baseDir . '/lib/private/Repair/SqliteAutoincrement.php', 'OC\\RichObjectStrings\\Validator' => $baseDir . '/lib/private/RichObjectStrings/Validator.php', 'OC\\Route\\CachingRouter' => $baseDir . '/lib/private/Route/CachingRouter.php', 'OC\\Route\\Route' => $baseDir . '/lib/private/Route/Route.php', 'OC\\Route\\Router' => $baseDir . '/lib/private/Route/Router.php', 'OC\\Search' => $baseDir . '/lib/private/Search.php', 'OC\\Search\\Provider\\File' => $baseDir . '/lib/private/Search/Provider/File.php', 'OC\\Search\\Result\\Audio' => $baseDir . '/lib/private/Search/Result/Audio.php', 'OC\\Search\\Result\\File' => $baseDir . '/lib/private/Search/Result/File.php', 'OC\\Search\\Result\\Folder' => $baseDir . '/lib/private/Search/Result/Folder.php', 'OC\\Search\\Result\\Image' => $baseDir . '/lib/private/Search/Result/Image.php', 'OC\\Security\\Bruteforce\\Throttler' => $baseDir . '/lib/private/Security/Bruteforce/Throttler.php', 'OC\\Security\\CSP\\ContentSecurityPolicy' => $baseDir . '/lib/private/Security/CSP/ContentSecurityPolicy.php', 'OC\\Security\\CSP\\ContentSecurityPolicyManager' => $baseDir . '/lib/private/Security/CSP/ContentSecurityPolicyManager.php', 'OC\\Security\\CSP\\ContentSecurityPolicyNonceManager' => $baseDir . '/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php', 'OC\\Security\\CSRF\\CsrfToken' => $baseDir . '/lib/private/Security/CSRF/CsrfToken.php', 'OC\\Security\\CSRF\\CsrfTokenGenerator' => $baseDir . '/lib/private/Security/CSRF/CsrfTokenGenerator.php', 'OC\\Security\\CSRF\\CsrfTokenManager' => $baseDir . '/lib/private/Security/CSRF/CsrfTokenManager.php', 'OC\\Security\\CSRF\\TokenStorage\\SessionStorage' => $baseDir . '/lib/private/Security/CSRF/TokenStorage/SessionStorage.php', 'OC\\Security\\Certificate' => $baseDir . '/lib/private/Security/Certificate.php', 'OC\\Security\\CertificateManager' => $baseDir . '/lib/private/Security/CertificateManager.php', 'OC\\Security\\CredentialsManager' => $baseDir . '/lib/private/Security/CredentialsManager.php', 'OC\\Security\\Crypto' => $baseDir . '/lib/private/Security/Crypto.php', 'OC\\Security\\Hasher' => $baseDir . '/lib/private/Security/Hasher.php', 'OC\\Security\\IdentityProof\\Key' => $baseDir . '/lib/private/Security/IdentityProof/Key.php', 'OC\\Security\\IdentityProof\\Manager' => $baseDir . '/lib/private/Security/IdentityProof/Manager.php', 'OC\\Security\\IdentityProof\\Signer' => $baseDir . '/lib/private/Security/IdentityProof/Signer.php', 'OC\\Security\\Normalizer\\IpAddress' => $baseDir . '/lib/private/Security/Normalizer/IpAddress.php', 'OC\\Security\\RateLimiting\\Backend\\IBackend' => $baseDir . '/lib/private/Security/RateLimiting/Backend/IBackend.php', 'OC\\Security\\RateLimiting\\Backend\\MemoryCache' => $baseDir . '/lib/private/Security/RateLimiting/Backend/MemoryCache.php', 'OC\\Security\\RateLimiting\\Exception\\RateLimitExceededException' => $baseDir . '/lib/private/Security/RateLimiting/Exception/RateLimitExceededException.php', 'OC\\Security\\RateLimiting\\Limiter' => $baseDir . '/lib/private/Security/RateLimiting/Limiter.php', 'OC\\Security\\SecureRandom' => $baseDir . '/lib/private/Security/SecureRandom.php', 'OC\\Security\\TrustedDomainHelper' => $baseDir . '/lib/private/Security/TrustedDomainHelper.php', 'OC\\Server' => $baseDir . '/lib/private/Server.php', 'OC\\ServerContainer' => $baseDir . '/lib/private/ServerContainer.php', 'OC\\ServerNotAvailableException' => $baseDir . '/lib/private/ServerNotAvailableException.php', 'OC\\ServiceUnavailableException' => $baseDir . '/lib/private/ServiceUnavailableException.php', 'OC\\Session\\CryptoSessionData' => $baseDir . '/lib/private/Session/CryptoSessionData.php', 'OC\\Session\\CryptoWrapper' => $baseDir . '/lib/private/Session/CryptoWrapper.php', 'OC\\Session\\Internal' => $baseDir . '/lib/private/Session/Internal.php', 'OC\\Session\\Memory' => $baseDir . '/lib/private/Session/Memory.php', 'OC\\Session\\Session' => $baseDir . '/lib/private/Session/Session.php', 'OC\\Settings\\Activity\\Provider' => $baseDir . '/settings/Activity/Provider.php', 'OC\\Settings\\Activity\\SecurityFilter' => $baseDir . '/settings/Activity/SecurityFilter.php', 'OC\\Settings\\Activity\\SecurityProvider' => $baseDir . '/settings/Activity/SecurityProvider.php', 'OC\\Settings\\Activity\\SecuritySetting' => $baseDir . '/settings/Activity/SecuritySetting.php', 'OC\\Settings\\Activity\\Setting' => $baseDir . '/settings/Activity/Setting.php', 'OC\\Settings\\Admin\\Additional' => $baseDir . '/lib/private/Settings/Admin/Additional.php', 'OC\\Settings\\Admin\\Encryption' => $baseDir . '/lib/private/Settings/Admin/Encryption.php', 'OC\\Settings\\Admin\\Server' => $baseDir . '/lib/private/Settings/Admin/Server.php', 'OC\\Settings\\Admin\\ServerDevNotice' => $baseDir . '/lib/private/Settings/Admin/ServerDevNotice.php', 'OC\\Settings\\Admin\\Sharing' => $baseDir . '/lib/private/Settings/Admin/Sharing.php', 'OC\\Settings\\Admin\\TipsTricks' => $baseDir . '/lib/private/Settings/Admin/TipsTricks.php', 'OC\\Settings\\Application' => $baseDir . '/settings/Application.php', 'OC\\Settings\\BackgroundJobs\\VerifyUserData' => $baseDir . '/settings/BackgroundJobs/VerifyUserData.php', 'OC\\Settings\\Controller\\AdminSettingsController' => $baseDir . '/settings/Controller/AdminSettingsController.php', 'OC\\Settings\\Controller\\AppSettingsController' => $baseDir . '/settings/Controller/AppSettingsController.php', 'OC\\Settings\\Controller\\AuthSettingsController' => $baseDir . '/settings/Controller/AuthSettingsController.php', 'OC\\Settings\\Controller\\CertificateController' => $baseDir . '/settings/Controller/CertificateController.php', 'OC\\Settings\\Controller\\ChangePasswordController' => $baseDir . '/settings/Controller/ChangePasswordController.php', 'OC\\Settings\\Controller\\CheckSetupController' => $baseDir . '/settings/Controller/CheckSetupController.php', 'OC\\Settings\\Controller\\EncryptionController' => $baseDir . '/settings/Controller/EncryptionController.php', 'OC\\Settings\\Controller\\GroupsController' => $baseDir . '/settings/Controller/GroupsController.php', 'OC\\Settings\\Controller\\LogSettingsController' => $baseDir . '/settings/Controller/LogSettingsController.php', 'OC\\Settings\\Controller\\MailSettingsController' => $baseDir . '/settings/Controller/MailSettingsController.php', 'OC\\Settings\\Controller\\SecuritySettingsController' => $baseDir . '/settings/Controller/SecuritySettingsController.php', 'OC\\Settings\\Controller\\UsersController' => $baseDir . '/settings/Controller/UsersController.php', 'OC\\Settings\\Hooks' => $baseDir . '/settings/Hooks.php', 'OC\\Settings\\Mailer\\NewUserMailHelper' => $baseDir . '/settings/Mailer/NewUserMailHelper.php', 'OC\\Settings\\Manager' => $baseDir . '/lib/private/Settings/Manager.php', 'OC\\Settings\\Mapper' => $baseDir . '/lib/private/Settings/Mapper.php', 'OC\\Settings\\Middleware\\SubadminMiddleware' => $baseDir . '/settings/Middleware/SubadminMiddleware.php', 'OC\\Settings\\RemoveOrphaned' => $baseDir . '/lib/private/Settings/RemoveOrphaned.php', 'OC\\Settings\\Section' => $baseDir . '/lib/private/Settings/Section.php', 'OC\\Setup' => $baseDir . '/lib/private/Setup.php', 'OC\\Setup\\AbstractDatabase' => $baseDir . '/lib/private/Setup/AbstractDatabase.php', 'OC\\Setup\\MySQL' => $baseDir . '/lib/private/Setup/MySQL.php', 'OC\\Setup\\OCI' => $baseDir . '/lib/private/Setup/OCI.php', 'OC\\Setup\\PostgreSQL' => $baseDir . '/lib/private/Setup/PostgreSQL.php', 'OC\\Setup\\Sqlite' => $baseDir . '/lib/private/Setup/Sqlite.php', 'OC\\Share20\\DefaultShareProvider' => $baseDir . '/lib/private/Share20/DefaultShareProvider.php', 'OC\\Share20\\Exception\\BackendError' => $baseDir . '/lib/private/Share20/Exception/BackendError.php', 'OC\\Share20\\Exception\\InvalidShare' => $baseDir . '/lib/private/Share20/Exception/InvalidShare.php', 'OC\\Share20\\Exception\\ProviderException' => $baseDir . '/lib/private/Share20/Exception/ProviderException.php', 'OC\\Share20\\Hooks' => $baseDir . '/lib/private/Share20/Hooks.php', 'OC\\Share20\\LegacyHooks' => $baseDir . '/lib/private/Share20/LegacyHooks.php', 'OC\\Share20\\Manager' => $baseDir . '/lib/private/Share20/Manager.php', 'OC\\Share20\\ProviderFactory' => $baseDir . '/lib/private/Share20/ProviderFactory.php', 'OC\\Share20\\Share' => $baseDir . '/lib/private/Share20/Share.php', 'OC\\Share20\\ShareHelper' => $baseDir . '/lib/private/Share20/ShareHelper.php', 'OC\\Share\\Constants' => $baseDir . '/lib/private/Share/Constants.php', 'OC\\Share\\Helper' => $baseDir . '/lib/private/Share/Helper.php', 'OC\\Share\\SearchResultSorter' => $baseDir . '/lib/private/Share/SearchResultSorter.php', 'OC\\Share\\Share' => $baseDir . '/lib/private/Share/Share.php', 'OC\\Streamer' => $baseDir . '/lib/private/Streamer.php', 'OC\\SubAdmin' => $baseDir . '/lib/private/SubAdmin.php', 'OC\\SystemConfig' => $baseDir . '/lib/private/SystemConfig.php', 'OC\\SystemTag\\ManagerFactory' => $baseDir . '/lib/private/SystemTag/ManagerFactory.php', 'OC\\SystemTag\\SystemTag' => $baseDir . '/lib/private/SystemTag/SystemTag.php', 'OC\\SystemTag\\SystemTagManager' => $baseDir . '/lib/private/SystemTag/SystemTagManager.php', 'OC\\SystemTag\\SystemTagObjectMapper' => $baseDir . '/lib/private/SystemTag/SystemTagObjectMapper.php', 'OC\\TagManager' => $baseDir . '/lib/private/TagManager.php', 'OC\\Tagging\\Tag' => $baseDir . '/lib/private/Tagging/Tag.php', 'OC\\Tagging\\TagMapper' => $baseDir . '/lib/private/Tagging/TagMapper.php', 'OC\\Tags' => $baseDir . '/lib/private/Tags.php', 'OC\\TempManager' => $baseDir . '/lib/private/TempManager.php', 'OC\\TemplateLayout' => $baseDir . '/lib/private/TemplateLayout.php', 'OC\\Template\\Base' => $baseDir . '/lib/private/Template/Base.php', 'OC\\Template\\CSSResourceLocator' => $baseDir . '/lib/private/Template/CSSResourceLocator.php', 'OC\\Template\\JSCombiner' => $baseDir . '/lib/private/Template/JSCombiner.php', 'OC\\Template\\JSConfigHelper' => $baseDir . '/lib/private/Template/JSConfigHelper.php', 'OC\\Template\\JSResourceLocator' => $baseDir . '/lib/private/Template/JSResourceLocator.php', 'OC\\Template\\ResourceLocator' => $baseDir . '/lib/private/Template/ResourceLocator.php', 'OC\\Template\\ResourceNotFoundException' => $baseDir . '/lib/private/Template/ResourceNotFoundException.php', 'OC\\Template\\SCSSCacher' => $baseDir . '/lib/private/Template/SCSSCacher.php', 'OC\\Template\\TemplateFileLocator' => $baseDir . '/lib/private/Template/TemplateFileLocator.php', 'OC\\URLGenerator' => $baseDir . '/lib/private/URLGenerator.php', 'OC\\Updater' => $baseDir . '/lib/private/Updater.php', 'OC\\Updater\\VersionCheck' => $baseDir . '/lib/private/Updater/VersionCheck.php', 'OC\\User\\Backend' => $baseDir . '/lib/private/User/Backend.php', 'OC\\User\\Database' => $baseDir . '/lib/private/User/Database.php', 'OC\\User\\LoginException' => $baseDir . '/lib/private/User/LoginException.php', 'OC\\User\\Manager' => $baseDir . '/lib/private/User/Manager.php', 'OC\\User\\NoUserException' => $baseDir . '/lib/private/User/NoUserException.php', 'OC\\User\\Session' => $baseDir . '/lib/private/User/Session.php', 'OC\\User\\User' => $baseDir . '/lib/private/User/User.php', ); composer/composer/autoload_psr4.php 0000604 00000000536 15247130453 0013517 0 ustar 00 <?php // autoload_psr4.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname(dirname($vendorDir)); return array( 'OC\\Settings\\' => array($baseDir . '/settings'), 'OC\\Core\\' => array($baseDir . '/core'), 'OC\\' => array($baseDir . '/lib/private'), 'OCP\\' => array($baseDir . '/lib/public'), ); composer/composer/autoload_real.php 0000604 00000003342 15247130453 0013550 0 ustar 00 <?php // autoload_real.php @generated by Composer class ComposerAutoloaderInit53792487c5a8370acc0b06b1a864ff4c { private static $loader; public static function loadClassLoader($class) { if ('Composer\Autoload\ClassLoader' === $class) { require __DIR__ . '/ClassLoader.php'; } } public static function getLoader() { if (null !== self::$loader) { return self::$loader; } spl_autoload_register(array('ComposerAutoloaderInit53792487c5a8370acc0b06b1a864ff4c', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit53792487c5a8370acc0b06b1a864ff4c', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } } $loader->register(true); return $loader; } } composer/composer/ClassLoader.php 0000604 00000032213 15247130453 0013130 0 ustar 00 <?php /* * This file is part of Composer. * * (c) Nils Adermann <naderman@naderman.de> * Jordi Boggiano <j.boggiano@seld.be> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Autoload; /** * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. * * $loader = new \Composer\Autoload\ClassLoader(); * * // register classes with namespaces * $loader->add('Symfony\Component', __DIR__.'/component'); * $loader->add('Symfony', __DIR__.'/framework'); * * // activate the autoloader * $loader->register(); * * // to enable searching the include path (eg. for PEAR packages) * $loader->setUseIncludePath(true); * * In this example, if you try to use a class in the Symfony\Component * namespace or one of its children (Symfony\Component\Console for instance), * the autoloader will first look for the class under the component/ * directory, and it will then fallback to the framework/ directory if not * found before giving up. * * This class is loosely based on the Symfony UniversalClassLoader. * * @author Fabien Potencier <fabien@symfony.com> * @author Jordi Boggiano <j.boggiano@seld.be> * @see http://www.php-fig.org/psr/psr-0/ * @see http://www.php-fig.org/psr/psr-4/ */ class ClassLoader { // PSR-4 private $prefixLengthsPsr4 = array(); private $prefixDirsPsr4 = array(); private $fallbackDirsPsr4 = array(); // PSR-0 private $prefixesPsr0 = array(); private $fallbackDirsPsr0 = array(); private $useIncludePath = false; private $classMap = array(); private $classMapAuthoritative = false; private $missingClasses = array(); private $apcuPrefix; public function getPrefixes() { if (!empty($this->prefixesPsr0)) { return call_user_func_array('array_merge', $this->prefixesPsr0); } return array(); } public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } public function getFallbackDirs() { return $this->fallbackDirsPsr0; } public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } public function getClassMap() { return $this->classMap; } /** * @param array $classMap Class to filename map */ public function addClassMap(array $classMap) { if ($this->classMap) { $this->classMap = array_merge($this->classMap, $classMap); } else { $this->classMap = $classMap; } } /** * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * * @param string $prefix The prefix * @param array|string $paths The PSR-0 root directories * @param bool $prepend Whether to prepend the directories */ public function add($prefix, $paths, $prepend = false) { if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( (array) $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, (array) $paths ); } return; } $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { $this->prefixesPsr0[$first][$prefix] = (array) $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( (array) $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], (array) $paths ); } } /** * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-4 base directories * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException */ public function addPsr4($prefix, $paths, $prepend = false) { if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirsPsr4 = array_merge( (array) $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, (array) $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { // Register directories for a new namespace. $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( (array) $paths, $this->prefixDirsPsr4[$prefix] ); } else { // Append directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], (array) $paths ); } } /** * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * * @param string $prefix The prefix * @param array|string $paths The PSR-0 base directories */ public function set($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr0 = (array) $paths; } else { $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; } } /** * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException */ public function setPsr4($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr4 = (array) $paths; } else { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } } /** * Turns on searching the include path for class files. * * @param bool $useIncludePath */ public function setUseIncludePath($useIncludePath) { $this->useIncludePath = $useIncludePath; } /** * Can be used to check if the autoloader uses the include path to check * for classes. * * @return bool */ public function getUseIncludePath() { return $this->useIncludePath; } /** * Turns off searching the prefix and fallback directories for classes * that have not been registered with the class map. * * @param bool $classMapAuthoritative */ public function setClassMapAuthoritative($classMapAuthoritative) { $this->classMapAuthoritative = $classMapAuthoritative; } /** * Should class lookup fail if not found in the current class map? * * @return bool */ public function isClassMapAuthoritative() { return $this->classMapAuthoritative; } /** * APCu prefix to use to cache found/not-found classes, if the extension is enabled. * * @param string|null $apcuPrefix */ public function setApcuPrefix($apcuPrefix) { $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; } /** * The APCu prefix in use, or null if APCu caching is not enabled. * * @return string|null */ public function getApcuPrefix() { return $this->apcuPrefix; } /** * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); } /** * Unregisters this instance as an autoloader. */ public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); } /** * Loads the given class or interface. * * @param string $class The name of the class * @return bool|null True if loaded, null otherwise */ public function loadClass($class) { if ($file = $this->findFile($class)) { includeFile($file); return true; } } /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class * * @return string|false The path if found, false otherwise */ public function findFile($class) { // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } if (null !== $this->apcuPrefix) { $file = apcu_fetch($this->apcuPrefix.$class, $hit); if ($hit) { return $file; } } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if (null !== $this->apcuPrefix) { apcu_add($this->apcuPrefix.$class, $file); } if (false === $file) { // Remember that this class does not exist. $this->missingClasses[$class] = true; } return $file; } private function findFileWithExtension($class, $ext) { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { $subPath = substr($subPath, 0, $lastPos); $search = $subPath.'\\'; if (isset($this->prefixDirsPsr4[$search])) { foreach ($this->prefixDirsPsr4[$search] as $dir) { $length = $this->prefixLengthsPsr4[$first][$search]; if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // PSR-0 lookup if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } // PSR-0 fallback dirs foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } // PSR-0 include paths. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } return false; } } /** * Scope isolated include. * * Prevents access to $this/self from included files. */ function includeFile($file) { include $file; } composer/composer/installed.json 0000604 00000000003 15247130453 0013065 0 ustar 00 [] composer/composer/LICENSE 0000604 00000002056 15247130453 0011232 0 ustar 00 Copyright (c) Nils Adermann, Jordi Boggiano Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. composer/composer/autoload_static.php 0000604 00000301767 15247130453 0014130 0 ustar 00 <?php // autoload_static.php @generated by Composer namespace Composer\Autoload; class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c { public static $prefixLengthsPsr4 = array ( 'O' => array ( 'OC\\Settings\\' => 12, 'OC\\Core\\' => 8, 'OC\\' => 3, 'OCP\\' => 4, ), ); public static $prefixDirsPsr4 = array ( 'OC\\Settings\\' => array ( 0 => __DIR__ . '/../../..' . '/settings', ), 'OC\\Core\\' => array ( 0 => __DIR__ . '/../../..' . '/core', ), 'OC\\' => array ( 0 => __DIR__ . '/../../..' . '/lib/private', ), 'OCP\\' => array ( 0 => __DIR__ . '/../../..' . '/lib/public', ), ); public static $classMap = array ( 'OCP\\API' => __DIR__ . '/../../..' . '/lib/public/API.php', 'OCP\\Activity\\IConsumer' => __DIR__ . '/../../..' . '/lib/public/Activity/IConsumer.php', 'OCP\\Activity\\IEvent' => __DIR__ . '/../../..' . '/lib/public/Activity/IEvent.php', 'OCP\\Activity\\IEventMerger' => __DIR__ . '/../../..' . '/lib/public/Activity/IEventMerger.php', 'OCP\\Activity\\IExtension' => __DIR__ . '/../../..' . '/lib/public/Activity/IExtension.php', 'OCP\\Activity\\IFilter' => __DIR__ . '/../../..' . '/lib/public/Activity/IFilter.php', 'OCP\\Activity\\IManager' => __DIR__ . '/../../..' . '/lib/public/Activity/IManager.php', 'OCP\\Activity\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Activity/IProvider.php', 'OCP\\Activity\\ISetting' => __DIR__ . '/../../..' . '/lib/public/Activity/ISetting.php', 'OCP\\App' => __DIR__ . '/../../..' . '/lib/public/App.php', 'OCP\\AppFramework\\ApiController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/ApiController.php', 'OCP\\AppFramework\\App' => __DIR__ . '/../../..' . '/lib/public/AppFramework/App.php', 'OCP\\AppFramework\\Controller' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Controller.php', 'OCP\\AppFramework\\Db\\DoesNotExistException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/DoesNotExistException.php', 'OCP\\AppFramework\\Db\\Entity' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/Entity.php', 'OCP\\AppFramework\\Db\\Mapper' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/Mapper.php', 'OCP\\AppFramework\\Db\\MultipleObjectsReturnedException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/MultipleObjectsReturnedException.php', 'OCP\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http.php', 'OCP\\AppFramework\\Http\\ContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/ContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\DataDisplayResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DataDisplayResponse.php', 'OCP\\AppFramework\\Http\\DataDownloadResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DataDownloadResponse.php', 'OCP\\AppFramework\\Http\\DataResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DataResponse.php', 'OCP\\AppFramework\\Http\\DownloadResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DownloadResponse.php', 'OCP\\AppFramework\\Http\\EmptyContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\FileDisplayResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/FileDisplayResponse.php', 'OCP\\AppFramework\\Http\\ICallbackResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/ICallbackResponse.php', 'OCP\\AppFramework\\Http\\IOutput' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/IOutput.php', 'OCP\\AppFramework\\Http\\JSONResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/JSONResponse.php', 'OCP\\AppFramework\\Http\\NotFoundResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/NotFoundResponse.php', 'OCP\\AppFramework\\Http\\OCSResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/OCSResponse.php', 'OCP\\AppFramework\\Http\\RedirectResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/RedirectResponse.php', 'OCP\\AppFramework\\Http\\Response' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Response.php', 'OCP\\AppFramework\\Http\\StreamResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StreamResponse.php', 'OCP\\AppFramework\\Http\\TemplateResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/TemplateResponse.php', 'OCP\\AppFramework\\IApi' => __DIR__ . '/../../..' . '/lib/public/AppFramework/IApi.php', 'OCP\\AppFramework\\IAppContainer' => __DIR__ . '/../../..' . '/lib/public/AppFramework/IAppContainer.php', 'OCP\\AppFramework\\Middleware' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Middleware.php', 'OCP\\AppFramework\\OCSController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/OCSController.php', 'OCP\\AppFramework\\OCS\\OCSBadRequestException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/OCS/OCSBadRequestException.php', 'OCP\\AppFramework\\OCS\\OCSException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/OCS/OCSException.php', 'OCP\\AppFramework\\OCS\\OCSForbiddenException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/OCS/OCSForbiddenException.php', 'OCP\\AppFramework\\OCS\\OCSNotFoundException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/OCS/OCSNotFoundException.php', 'OCP\\AppFramework\\QueryException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/QueryException.php', 'OCP\\AppFramework\\Utility\\IControllerMethodReflector' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/IControllerMethodReflector.php', 'OCP\\AppFramework\\Utility\\ITimeFactory' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/ITimeFactory.php', 'OCP\\App\\AppPathNotFoundException' => __DIR__ . '/../../..' . '/lib/public/App/AppPathNotFoundException.php', 'OCP\\App\\IAppManager' => __DIR__ . '/../../..' . '/lib/public/App/IAppManager.php', 'OCP\\App\\ManagerEvent' => __DIR__ . '/../../..' . '/lib/public/App/ManagerEvent.php', 'OCP\\Authentication\\Exceptions\\CredentialsUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Authentication/Exceptions/CredentialsUnavailableException.php', 'OCP\\Authentication\\Exceptions\\PasswordUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Authentication/Exceptions/PasswordUnavailableException.php', 'OCP\\Authentication\\IApacheBackend' => __DIR__ . '/../../..' . '/lib/public/Authentication/IApacheBackend.php', 'OCP\\Authentication\\LoginCredentials\\ICredentials' => __DIR__ . '/../../..' . '/lib/public/Authentication/LoginCredentials/ICredentials.php', 'OCP\\Authentication\\LoginCredentials\\IStore' => __DIR__ . '/../../..' . '/lib/public/Authentication/LoginCredentials/IStore.php', 'OCP\\Authentication\\TwoFactorAuth\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/IProvider.php', 'OCP\\Authentication\\TwoFactorAuth\\TwoFactorException' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/TwoFactorException.php', 'OCP\\AutoloadNotAllowedException' => __DIR__ . '/../../..' . '/lib/public/AutoloadNotAllowedException.php', 'OCP\\BackgroundJob' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob.php', 'OCP\\BackgroundJob\\IJob' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/IJob.php', 'OCP\\BackgroundJob\\IJobList' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/IJobList.php', 'OCP\\Capabilities\\ICapability' => __DIR__ . '/../../..' . '/lib/public/Capabilities/ICapability.php', 'OCP\\Command\\IBus' => __DIR__ . '/../../..' . '/lib/public/Command/IBus.php', 'OCP\\Command\\ICommand' => __DIR__ . '/../../..' . '/lib/public/Command/ICommand.php', 'OCP\\Comments\\CommentsEntityEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEntityEvent.php', 'OCP\\Comments\\CommentsEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEvent.php', 'OCP\\Comments\\IComment' => __DIR__ . '/../../..' . '/lib/public/Comments/IComment.php', 'OCP\\Comments\\ICommentsEventHandler' => __DIR__ . '/../../..' . '/lib/public/Comments/ICommentsEventHandler.php', 'OCP\\Comments\\ICommentsManager' => __DIR__ . '/../../..' . '/lib/public/Comments/ICommentsManager.php', 'OCP\\Comments\\ICommentsManagerFactory' => __DIR__ . '/../../..' . '/lib/public/Comments/ICommentsManagerFactory.php', 'OCP\\Comments\\IllegalIDChangeException' => __DIR__ . '/../../..' . '/lib/public/Comments/IllegalIDChangeException.php', 'OCP\\Comments\\MessageTooLongException' => __DIR__ . '/../../..' . '/lib/public/Comments/MessageTooLongException.php', 'OCP\\Comments\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Comments/NotFoundException.php', 'OCP\\Config' => __DIR__ . '/../../..' . '/lib/public/Config.php', 'OCP\\Console\\ConsoleEvent' => __DIR__ . '/../../..' . '/lib/public/Console/ConsoleEvent.php', 'OCP\\Constants' => __DIR__ . '/../../..' . '/lib/public/Constants.php', 'OCP\\Contacts' => __DIR__ . '/../../..' . '/lib/public/Contacts.php', 'OCP\\Contacts\\ContactsMenu\\IAction' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IAction.php', 'OCP\\Contacts\\ContactsMenu\\IActionFactory' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IActionFactory.php', 'OCP\\Contacts\\ContactsMenu\\IEntry' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IEntry.php', 'OCP\\Contacts\\ContactsMenu\\ILinkAction' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/ILinkAction.php', 'OCP\\Contacts\\ContactsMenu\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IProvider.php', 'OCP\\Contacts\\IManager' => __DIR__ . '/../../..' . '/lib/public/Contacts/IManager.php', 'OCP\\DB' => __DIR__ . '/../../..' . '/lib/public/DB.php', 'OCP\\DB\\QueryBuilder\\ICompositeExpression' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/ICompositeExpression.php', 'OCP\\DB\\QueryBuilder\\IExpressionBuilder' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IExpressionBuilder.php', 'OCP\\DB\\QueryBuilder\\IFunctionBuilder' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IFunctionBuilder.php', 'OCP\\DB\\QueryBuilder\\ILiteral' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/ILiteral.php', 'OCP\\DB\\QueryBuilder\\IParameter' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IParameter.php', 'OCP\\DB\\QueryBuilder\\IQueryBuilder' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IQueryBuilder.php', 'OCP\\DB\\QueryBuilder\\IQueryFunction' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/IQueryFunction.php', 'OCP\\Defaults' => __DIR__ . '/../../..' . '/lib/public/Defaults.php', 'OCP\\Diagnostics\\IEvent' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IEvent.php', 'OCP\\Diagnostics\\IEventLogger' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IEventLogger.php', 'OCP\\Diagnostics\\IQuery' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IQuery.php', 'OCP\\Diagnostics\\IQueryLogger' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IQueryLogger.php', 'OCP\\Encryption\\Exceptions\\GenericEncryptionException' => __DIR__ . '/../../..' . '/lib/public/Encryption/Exceptions/GenericEncryptionException.php', 'OCP\\Encryption\\IEncryptionModule' => __DIR__ . '/../../..' . '/lib/public/Encryption/IEncryptionModule.php', 'OCP\\Encryption\\IFile' => __DIR__ . '/../../..' . '/lib/public/Encryption/IFile.php', 'OCP\\Encryption\\IManager' => __DIR__ . '/../../..' . '/lib/public/Encryption/IManager.php', 'OCP\\Encryption\\Keys\\IStorage' => __DIR__ . '/../../..' . '/lib/public/Encryption/Keys/IStorage.php', 'OCP\\Federation\\ICloudId' => __DIR__ . '/../../..' . '/lib/public/Federation/ICloudId.php', 'OCP\\Federation\\ICloudIdManager' => __DIR__ . '/../../..' . '/lib/public/Federation/ICloudIdManager.php', 'OCP\\Files' => __DIR__ . '/../../..' . '/lib/public/Files.php', 'OCP\\Files\\AlreadyExistsException' => __DIR__ . '/../../..' . '/lib/public/Files/AlreadyExistsException.php', 'OCP\\Files\\Cache\\ICache' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/ICache.php', 'OCP\\Files\\Cache\\ICacheEntry' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/ICacheEntry.php', 'OCP\\Files\\Cache\\IPropagator' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/IPropagator.php', 'OCP\\Files\\Cache\\IScanner' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/IScanner.php', 'OCP\\Files\\Cache\\IUpdater' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/IUpdater.php', 'OCP\\Files\\Cache\\IWatcher' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/IWatcher.php', 'OCP\\Files\\Config\\ICachedMountInfo' => __DIR__ . '/../../..' . '/lib/public/Files/Config/ICachedMountInfo.php', 'OCP\\Files\\Config\\IHomeMountProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IHomeMountProvider.php', 'OCP\\Files\\Config\\IMountProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IMountProvider.php', 'OCP\\Files\\Config\\IMountProviderCollection' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IMountProviderCollection.php', 'OCP\\Files\\Config\\IUserMountCache' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IUserMountCache.php', 'OCP\\Files\\EmptyFileNameException' => __DIR__ . '/../../..' . '/lib/public/Files/EmptyFileNameException.php', 'OCP\\Files\\EntityTooLargeException' => __DIR__ . '/../../..' . '/lib/public/Files/EntityTooLargeException.php', 'OCP\\Files\\File' => __DIR__ . '/../../..' . '/lib/public/Files/File.php', 'OCP\\Files\\FileInfo' => __DIR__ . '/../../..' . '/lib/public/Files/FileInfo.php', 'OCP\\Files\\FileNameTooLongException' => __DIR__ . '/../../..' . '/lib/public/Files/FileNameTooLongException.php', 'OCP\\Files\\Folder' => __DIR__ . '/../../..' . '/lib/public/Files/Folder.php', 'OCP\\Files\\ForbiddenException' => __DIR__ . '/../../..' . '/lib/public/Files/ForbiddenException.php', 'OCP\\Files\\IAppData' => __DIR__ . '/../../..' . '/lib/public/Files/IAppData.php', 'OCP\\Files\\IHomeStorage' => __DIR__ . '/../../..' . '/lib/public/Files/IHomeStorage.php', 'OCP\\Files\\IMimeTypeDetector' => __DIR__ . '/../../..' . '/lib/public/Files/IMimeTypeDetector.php', 'OCP\\Files\\IMimeTypeLoader' => __DIR__ . '/../../..' . '/lib/public/Files/IMimeTypeLoader.php', 'OCP\\Files\\IRootFolder' => __DIR__ . '/../../..' . '/lib/public/Files/IRootFolder.php', 'OCP\\Files\\InvalidCharacterInPathException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidCharacterInPathException.php', 'OCP\\Files\\InvalidContentException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidContentException.php', 'OCP\\Files\\InvalidDirectoryException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidDirectoryException.php', 'OCP\\Files\\InvalidPathException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidPathException.php', 'OCP\\Files\\LockNotAcquiredException' => __DIR__ . '/../../..' . '/lib/public/Files/LockNotAcquiredException.php', 'OCP\\Files\\Mount\\IMountManager' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountManager.php', 'OCP\\Files\\Mount\\IMountPoint' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountPoint.php', 'OCP\\Files\\Node' => __DIR__ . '/../../..' . '/lib/public/Files/Node.php', 'OCP\\Files\\NotEnoughSpaceException' => __DIR__ . '/../../..' . '/lib/public/Files/NotEnoughSpaceException.php', 'OCP\\Files\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Files/NotFoundException.php', 'OCP\\Files\\NotPermittedException' => __DIR__ . '/../../..' . '/lib/public/Files/NotPermittedException.php', 'OCP\\Files\\Notify\\IChange' => __DIR__ . '/../../..' . '/lib/public/Files/Notify/IChange.php', 'OCP\\Files\\Notify\\INotifyHandler' => __DIR__ . '/../../..' . '/lib/public/Files/Notify/INotifyHandler.php', 'OCP\\Files\\Notify\\IRenameChange' => __DIR__ . '/../../..' . '/lib/public/Files/Notify/IRenameChange.php', 'OCP\\Files\\ObjectStore\\IObjectStore' => __DIR__ . '/../../..' . '/lib/public/Files/ObjectStore/IObjectStore.php', 'OCP\\Files\\ReservedWordException' => __DIR__ . '/../../..' . '/lib/public/Files/ReservedWordException.php', 'OCP\\Files\\Search\\ISearchBinaryOperator' => __DIR__ . '/../../..' . '/lib/public/Files/Search/ISearchBinaryOperator.php', 'OCP\\Files\\Search\\ISearchComparison' => __DIR__ . '/../../..' . '/lib/public/Files/Search/ISearchComparison.php', 'OCP\\Files\\Search\\ISearchOperator' => __DIR__ . '/../../..' . '/lib/public/Files/Search/ISearchOperator.php', 'OCP\\Files\\Search\\ISearchOrder' => __DIR__ . '/../../..' . '/lib/public/Files/Search/ISearchOrder.php', 'OCP\\Files\\Search\\ISearchQuery' => __DIR__ . '/../../..' . '/lib/public/Files/Search/ISearchQuery.php', 'OCP\\Files\\SimpleFS\\ISimpleFile' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleFile.php', 'OCP\\Files\\SimpleFS\\ISimpleFolder' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleFolder.php', 'OCP\\Files\\SimpleFS\\ISimpleRoot' => __DIR__ . '/../../..' . '/lib/public/Files/SimpleFS/ISimpleRoot.php', 'OCP\\Files\\Storage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage.php', 'OCP\\Files\\StorageAuthException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageAuthException.php', 'OCP\\Files\\StorageBadConfigException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageBadConfigException.php', 'OCP\\Files\\StorageConnectionException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageConnectionException.php', 'OCP\\Files\\StorageInvalidException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageInvalidException.php', 'OCP\\Files\\StorageNotAvailableException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageNotAvailableException.php', 'OCP\\Files\\StorageTimeoutException' => __DIR__ . '/../../..' . '/lib/public/Files/StorageTimeoutException.php', 'OCP\\Files\\Storage\\ILockingStorage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/ILockingStorage.php', 'OCP\\Files\\Storage\\INotifyStorage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/INotifyStorage.php', 'OCP\\Files\\Storage\\IStorage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/IStorage.php', 'OCP\\Files\\Storage\\IStorageFactory' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/IStorageFactory.php', 'OCP\\Files\\UnseekableException' => __DIR__ . '/../../..' . '/lib/public/Files/UnseekableException.php', 'OCP\\GlobalScale\\IConfig' => __DIR__ . '/../../..' . '/lib/public/GlobalScale/IConfig.php', 'OCP\\GroupInterface' => __DIR__ . '/../../..' . '/lib/public/GroupInterface.php', 'OCP\\Http\\Client\\IClient' => __DIR__ . '/../../..' . '/lib/public/Http/Client/IClient.php', 'OCP\\Http\\Client\\IClientService' => __DIR__ . '/../../..' . '/lib/public/Http/Client/IClientService.php', 'OCP\\Http\\Client\\IResponse' => __DIR__ . '/../../..' . '/lib/public/Http/Client/IResponse.php', 'OCP\\IAddressBook' => __DIR__ . '/../../..' . '/lib/public/IAddressBook.php', 'OCP\\IAppConfig' => __DIR__ . '/../../..' . '/lib/public/IAppConfig.php', 'OCP\\IAvatar' => __DIR__ . '/../../..' . '/lib/public/IAvatar.php', 'OCP\\IAvatarManager' => __DIR__ . '/../../..' . '/lib/public/IAvatarManager.php', 'OCP\\ICache' => __DIR__ . '/../../..' . '/lib/public/ICache.php', 'OCP\\ICacheFactory' => __DIR__ . '/../../..' . '/lib/public/ICacheFactory.php', 'OCP\\ICertificate' => __DIR__ . '/../../..' . '/lib/public/ICertificate.php', 'OCP\\ICertificateManager' => __DIR__ . '/../../..' . '/lib/public/ICertificateManager.php', 'OCP\\IConfig' => __DIR__ . '/../../..' . '/lib/public/IConfig.php', 'OCP\\IContainer' => __DIR__ . '/../../..' . '/lib/public/IContainer.php', 'OCP\\IDBConnection' => __DIR__ . '/../../..' . '/lib/public/IDBConnection.php', 'OCP\\IDateTimeFormatter' => __DIR__ . '/../../..' . '/lib/public/IDateTimeFormatter.php', 'OCP\\IDateTimeZone' => __DIR__ . '/../../..' . '/lib/public/IDateTimeZone.php', 'OCP\\IEventSource' => __DIR__ . '/../../..' . '/lib/public/IEventSource.php', 'OCP\\IGroup' => __DIR__ . '/../../..' . '/lib/public/IGroup.php', 'OCP\\IGroupManager' => __DIR__ . '/../../..' . '/lib/public/IGroupManager.php', 'OCP\\IHelper' => __DIR__ . '/../../..' . '/lib/public/IHelper.php', 'OCP\\IImage' => __DIR__ . '/../../..' . '/lib/public/IImage.php', 'OCP\\IL10N' => __DIR__ . '/../../..' . '/lib/public/IL10N.php', 'OCP\\ILogger' => __DIR__ . '/../../..' . '/lib/public/ILogger.php', 'OCP\\IMemcache' => __DIR__ . '/../../..' . '/lib/public/IMemcache.php', 'OCP\\IMemcacheTTL' => __DIR__ . '/../../..' . '/lib/public/IMemcacheTTL.php', 'OCP\\INavigationManager' => __DIR__ . '/../../..' . '/lib/public/INavigationManager.php', 'OCP\\IPreview' => __DIR__ . '/../../..' . '/lib/public/IPreview.php', 'OCP\\IRequest' => __DIR__ . '/../../..' . '/lib/public/IRequest.php', 'OCP\\ISearch' => __DIR__ . '/../../..' . '/lib/public/ISearch.php', 'OCP\\IServerContainer' => __DIR__ . '/../../..' . '/lib/public/IServerContainer.php', 'OCP\\ISession' => __DIR__ . '/../../..' . '/lib/public/ISession.php', 'OCP\\ITagManager' => __DIR__ . '/../../..' . '/lib/public/ITagManager.php', 'OCP\\ITags' => __DIR__ . '/../../..' . '/lib/public/ITags.php', 'OCP\\ITempManager' => __DIR__ . '/../../..' . '/lib/public/ITempManager.php', 'OCP\\IURLGenerator' => __DIR__ . '/../../..' . '/lib/public/IURLGenerator.php', 'OCP\\IUser' => __DIR__ . '/../../..' . '/lib/public/IUser.php', 'OCP\\IUserBackend' => __DIR__ . '/../../..' . '/lib/public/IUserBackend.php', 'OCP\\IUserManager' => __DIR__ . '/../../..' . '/lib/public/IUserManager.php', 'OCP\\IUserSession' => __DIR__ . '/../../..' . '/lib/public/IUserSession.php', 'OCP\\Image' => __DIR__ . '/../../..' . '/lib/public/Image.php', 'OCP\\JSON' => __DIR__ . '/../../..' . '/lib/public/JSON.php', 'OCP\\L10N\\IFactory' => __DIR__ . '/../../..' . '/lib/public/L10N/IFactory.php', 'OCP\\LDAP\\IDeletionFlagSupport' => __DIR__ . '/../../..' . '/lib/public/LDAP/IDeletionFlagSupport.php', 'OCP\\LDAP\\ILDAPProvider' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProvider.php', 'OCP\\LDAP\\ILDAPProviderFactory' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProviderFactory.php', 'OCP\\Lock\\ILockingProvider' => __DIR__ . '/../../..' . '/lib/public/Lock/ILockingProvider.php', 'OCP\\Lock\\LockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/LockedException.php', 'OCP\\Lockdown\\ILockdownManager' => __DIR__ . '/../../..' . '/lib/public/Lockdown/ILockdownManager.php', 'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php', 'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php', 'OCP\\Migration\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Migration/IOutput.php', 'OCP\\Migration\\IRepairStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IRepairStep.php', 'OCP\\Notification\\IAction' => __DIR__ . '/../../..' . '/lib/public/Notification/IAction.php', 'OCP\\Notification\\IApp' => __DIR__ . '/../../..' . '/lib/public/Notification/IApp.php', 'OCP\\Notification\\IManager' => __DIR__ . '/../../..' . '/lib/public/Notification/IManager.php', 'OCP\\Notification\\INotification' => __DIR__ . '/../../..' . '/lib/public/Notification/INotification.php', 'OCP\\Notification\\INotifier' => __DIR__ . '/../../..' . '/lib/public/Notification/INotifier.php', 'OCP\\OCS\\IDiscoveryService' => __DIR__ . '/../../..' . '/lib/public/OCS/IDiscoveryService.php', 'OCP\\PreConditionNotMetException' => __DIR__ . '/../../..' . '/lib/public/PreConditionNotMetException.php', 'OCP\\Preview\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Preview/IProvider.php', 'OCP\\Response' => __DIR__ . '/../../..' . '/lib/public/Response.php', 'OCP\\RichObjectStrings\\Definitions' => __DIR__ . '/../../..' . '/lib/public/RichObjectStrings/Definitions.php', 'OCP\\RichObjectStrings\\IValidator' => __DIR__ . '/../../..' . '/lib/public/RichObjectStrings/IValidator.php', 'OCP\\RichObjectStrings\\InvalidObjectExeption' => __DIR__ . '/../../..' . '/lib/public/RichObjectStrings/InvalidObjectExeption.php', 'OCP\\Route\\IRoute' => __DIR__ . '/../../..' . '/lib/public/Route/IRoute.php', 'OCP\\Route\\IRouter' => __DIR__ . '/../../..' . '/lib/public/Route/IRouter.php', 'OCP\\SabrePluginEvent' => __DIR__ . '/../../..' . '/lib/public/SabrePluginEvent.php', 'OCP\\SabrePluginException' => __DIR__ . '/../../..' . '/lib/public/SabrePluginException.php', 'OCP\\Search\\PagedProvider' => __DIR__ . '/../../..' . '/lib/public/Search/PagedProvider.php', 'OCP\\Search\\Provider' => __DIR__ . '/../../..' . '/lib/public/Search/Provider.php', 'OCP\\Search\\Result' => __DIR__ . '/../../..' . '/lib/public/Search/Result.php', 'OCP\\Security\\IContentSecurityPolicyManager' => __DIR__ . '/../../..' . '/lib/public/Security/IContentSecurityPolicyManager.php', 'OCP\\Security\\ICredentialsManager' => __DIR__ . '/../../..' . '/lib/public/Security/ICredentialsManager.php', 'OCP\\Security\\ICrypto' => __DIR__ . '/../../..' . '/lib/public/Security/ICrypto.php', 'OCP\\Security\\IHasher' => __DIR__ . '/../../..' . '/lib/public/Security/IHasher.php', 'OCP\\Security\\ISecureRandom' => __DIR__ . '/../../..' . '/lib/public/Security/ISecureRandom.php', 'OCP\\Security\\StringUtils' => __DIR__ . '/../../..' . '/lib/public/Security/StringUtils.php', 'OCP\\Session\\Exceptions\\SessionNotAvailableException' => __DIR__ . '/../../..' . '/lib/public/Session/Exceptions/SessionNotAvailableException.php', 'OCP\\Settings\\IIconSection' => __DIR__ . '/../../..' . '/lib/public/Settings/IIconSection.php', 'OCP\\Settings\\IManager' => __DIR__ . '/../../..' . '/lib/public/Settings/IManager.php', 'OCP\\Settings\\ISection' => __DIR__ . '/../../..' . '/lib/public/Settings/ISection.php', 'OCP\\Settings\\ISettings' => __DIR__ . '/../../..' . '/lib/public/Settings/ISettings.php', 'OCP\\Share' => __DIR__ . '/../../..' . '/lib/public/Share.php', 'OCP\\Share\\Exceptions\\GenericShareException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/GenericShareException.php', 'OCP\\Share\\Exceptions\\IllegalIDChangeException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/IllegalIDChangeException.php', 'OCP\\Share\\Exceptions\\ShareNotFound' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/ShareNotFound.php', 'OCP\\Share\\IManager' => __DIR__ . '/../../..' . '/lib/public/Share/IManager.php', 'OCP\\Share\\IProviderFactory' => __DIR__ . '/../../..' . '/lib/public/Share/IProviderFactory.php', 'OCP\\Share\\IShare' => __DIR__ . '/../../..' . '/lib/public/Share/IShare.php', 'OCP\\Share\\IShareHelper' => __DIR__ . '/../../..' . '/lib/public/Share/IShareHelper.php', 'OCP\\Share\\IShareProvider' => __DIR__ . '/../../..' . '/lib/public/Share/IShareProvider.php', 'OCP\\Share_Backend' => __DIR__ . '/../../..' . '/lib/public/Share_Backend.php', 'OCP\\Share_Backend_Collection' => __DIR__ . '/../../..' . '/lib/public/Share_Backend_Collection.php', 'OCP\\Share_Backend_File_Dependent' => __DIR__ . '/../../..' . '/lib/public/Share_Backend_File_Dependent.php', 'OCP\\SystemTag\\ISystemTag' => __DIR__ . '/../../..' . '/lib/public/SystemTag/ISystemTag.php', 'OCP\\SystemTag\\ISystemTagManager' => __DIR__ . '/../../..' . '/lib/public/SystemTag/ISystemTagManager.php', 'OCP\\SystemTag\\ISystemTagManagerFactory' => __DIR__ . '/../../..' . '/lib/public/SystemTag/ISystemTagManagerFactory.php', 'OCP\\SystemTag\\ISystemTagObjectMapper' => __DIR__ . '/../../..' . '/lib/public/SystemTag/ISystemTagObjectMapper.php', 'OCP\\SystemTag\\ManagerEvent' => __DIR__ . '/../../..' . '/lib/public/SystemTag/ManagerEvent.php', 'OCP\\SystemTag\\MapperEvent' => __DIR__ . '/../../..' . '/lib/public/SystemTag/MapperEvent.php', 'OCP\\SystemTag\\SystemTagsEntityEvent' => __DIR__ . '/../../..' . '/lib/public/SystemTag/SystemTagsEntityEvent.php', 'OCP\\SystemTag\\TagAlreadyExistsException' => __DIR__ . '/../../..' . '/lib/public/SystemTag/TagAlreadyExistsException.php', 'OCP\\SystemTag\\TagNotFoundException' => __DIR__ . '/../../..' . '/lib/public/SystemTag/TagNotFoundException.php', 'OCP\\Template' => __DIR__ . '/../../..' . '/lib/public/Template.php', 'OCP\\User' => __DIR__ . '/../../..' . '/lib/public/User.php', 'OCP\\UserInterface' => __DIR__ . '/../../..' . '/lib/public/UserInterface.php', 'OCP\\Util' => __DIR__ . '/../../..' . '/lib/public/Util.php', 'OCP\\WorkflowEngine\\ICheck' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/ICheck.php', 'OCP\\WorkflowEngine\\IManager' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IManager.php', 'OCP\\WorkflowEngine\\IOperation' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IOperation.php', 'OC\\Accounts\\AccountManager' => __DIR__ . '/../../..' . '/lib/private/Accounts/AccountManager.php', 'OC\\Accounts\\Hooks' => __DIR__ . '/../../..' . '/lib/private/Accounts/Hooks.php', 'OC\\Activity\\Event' => __DIR__ . '/../../..' . '/lib/private/Activity/Event.php', 'OC\\Activity\\EventMerger' => __DIR__ . '/../../..' . '/lib/private/Activity/EventMerger.php', 'OC\\Activity\\LegacyFilter' => __DIR__ . '/../../..' . '/lib/private/Activity/LegacyFilter.php', 'OC\\Activity\\LegacySetting' => __DIR__ . '/../../..' . '/lib/private/Activity/LegacySetting.php', 'OC\\Activity\\Manager' => __DIR__ . '/../../..' . '/lib/private/Activity/Manager.php', 'OC\\AllConfig' => __DIR__ . '/../../..' . '/lib/private/AllConfig.php', 'OC\\AppConfig' => __DIR__ . '/../../..' . '/lib/private/AppConfig.php', 'OC\\AppFramework\\App' => __DIR__ . '/../../..' . '/lib/private/AppFramework/App.php', 'OC\\AppFramework\\Core\\API' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Core/API.php', 'OC\\AppFramework\\DependencyInjection\\DIContainer' => __DIR__ . '/../../..' . '/lib/private/AppFramework/DependencyInjection/DIContainer.php', 'OC\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http.php', 'OC\\AppFramework\\Http\\Dispatcher' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Dispatcher.php', 'OC\\AppFramework\\Http\\Output' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Output.php', 'OC\\AppFramework\\Http\\Request' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Request.php', 'OC\\AppFramework\\Middleware\\MiddlewareDispatcher' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php', 'OC\\AppFramework\\Middleware\\OCSMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/OCSMiddleware.php', 'OC\\AppFramework\\Middleware\\Security\\BruteForceMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php', 'OC\\AppFramework\\Middleware\\Security\\CORSMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\AppNotEnabledException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\CrossSiteRequestForgeryException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotAdminException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotConfirmedException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotLoggedInException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\SecurityException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php', 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\StrictCookieMissingException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php', 'OC\\AppFramework\\Middleware\\Security\\RateLimitingMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php', 'OC\\AppFramework\\Middleware\\Security\\SecurityMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php', 'OC\\AppFramework\\Middleware\\SessionMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/SessionMiddleware.php', 'OC\\AppFramework\\OCS\\BaseResponse' => __DIR__ . '/../../..' . '/lib/private/AppFramework/OCS/BaseResponse.php', 'OC\\AppFramework\\OCS\\V1Response' => __DIR__ . '/../../..' . '/lib/private/AppFramework/OCS/V1Response.php', 'OC\\AppFramework\\OCS\\V2Response' => __DIR__ . '/../../..' . '/lib/private/AppFramework/OCS/V2Response.php', 'OC\\AppFramework\\Routing\\RouteActionHandler' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteActionHandler.php', 'OC\\AppFramework\\Routing\\RouteConfig' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteConfig.php', 'OC\\AppFramework\\Utility\\ControllerMethodReflector' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/ControllerMethodReflector.php', 'OC\\AppFramework\\Utility\\SimpleContainer' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/SimpleContainer.php', 'OC\\AppFramework\\Utility\\TimeFactory' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/TimeFactory.php', 'OC\\AppHelper' => __DIR__ . '/../../..' . '/lib/private/AppHelper.php', 'OC\\App\\AppManager' => __DIR__ . '/../../..' . '/lib/private/App/AppManager.php', 'OC\\App\\AppStore\\Bundles\\Bundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/Bundle.php', 'OC\\App\\AppStore\\Bundles\\BundleFetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/BundleFetcher.php', 'OC\\App\\AppStore\\Bundles\\CoreBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/CoreBundle.php', 'OC\\App\\AppStore\\Bundles\\EducationBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/EducationBundle.php', 'OC\\App\\AppStore\\Bundles\\EnterpriseBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/EnterpriseBundle.php', 'OC\\App\\AppStore\\Bundles\\GroupwareBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/GroupwareBundle.php', 'OC\\App\\AppStore\\Bundles\\SocialSharingBundle' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Bundles/SocialSharingBundle.php', 'OC\\App\\AppStore\\Fetcher\\AppFetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/AppFetcher.php', 'OC\\App\\AppStore\\Fetcher\\CategoryFetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/CategoryFetcher.php', 'OC\\App\\AppStore\\Fetcher\\Fetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/Fetcher.php', 'OC\\App\\AppStore\\Version\\Version' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Version/Version.php', 'OC\\App\\AppStore\\Version\\VersionParser' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Version/VersionParser.php', 'OC\\App\\CodeChecker\\AbstractCheck' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/AbstractCheck.php', 'OC\\App\\CodeChecker\\CodeChecker' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/CodeChecker.php', 'OC\\App\\CodeChecker\\DatabaseSchemaChecker' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/DatabaseSchemaChecker.php', 'OC\\App\\CodeChecker\\DeprecationCheck' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/DeprecationCheck.php', 'OC\\App\\CodeChecker\\EmptyCheck' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/EmptyCheck.php', 'OC\\App\\CodeChecker\\ICheck' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/ICheck.php', 'OC\\App\\CodeChecker\\InfoChecker' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/InfoChecker.php', 'OC\\App\\CodeChecker\\LanguageParseChecker' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/LanguageParseChecker.php', 'OC\\App\\CodeChecker\\NodeVisitor' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/NodeVisitor.php', 'OC\\App\\CodeChecker\\PrivateCheck' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/PrivateCheck.php', 'OC\\App\\CodeChecker\\StrongComparisonCheck' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/StrongComparisonCheck.php', 'OC\\App\\DependencyAnalyzer' => __DIR__ . '/../../..' . '/lib/private/App/DependencyAnalyzer.php', 'OC\\App\\InfoParser' => __DIR__ . '/../../..' . '/lib/private/App/InfoParser.php', 'OC\\App\\Platform' => __DIR__ . '/../../..' . '/lib/private/App/Platform.php', 'OC\\App\\PlatformRepository' => __DIR__ . '/../../..' . '/lib/private/App/PlatformRepository.php', 'OC\\Archive\\Archive' => __DIR__ . '/../../..' . '/lib/private/Archive/Archive.php', 'OC\\Archive\\TAR' => __DIR__ . '/../../..' . '/lib/private/Archive/TAR.php', 'OC\\Archive\\ZIP' => __DIR__ . '/../../..' . '/lib/private/Archive/ZIP.php', 'OC\\Authentication\\Exceptions\\InvalidTokenException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/InvalidTokenException.php', 'OC\\Authentication\\Exceptions\\LoginRequiredException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/LoginRequiredException.php', 'OC\\Authentication\\Exceptions\\PasswordLoginForbiddenException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php', 'OC\\Authentication\\Exceptions\\PasswordlessTokenException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/PasswordlessTokenException.php', 'OC\\Authentication\\Exceptions\\TwoFactorAuthRequiredException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php', 'OC\\Authentication\\Exceptions\\UserAlreadyLoggedInException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php', 'OC\\Authentication\\LoginCredentials\\Credentials' => __DIR__ . '/../../..' . '/lib/private/Authentication/LoginCredentials/Credentials.php', 'OC\\Authentication\\LoginCredentials\\Store' => __DIR__ . '/../../..' . '/lib/private/Authentication/LoginCredentials/Store.php', 'OC\\Authentication\\Token\\DefaultToken' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultToken.php', 'OC\\Authentication\\Token\\DefaultTokenCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenCleanupJob.php', 'OC\\Authentication\\Token\\DefaultTokenMapper' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenMapper.php', 'OC\\Authentication\\Token\\DefaultTokenProvider' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenProvider.php', 'OC\\Authentication\\Token\\IProvider' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/IProvider.php', 'OC\\Authentication\\Token\\IToken' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/IToken.php', 'OC\\Authentication\\TwoFactorAuth\\Manager' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/Manager.php', 'OC\\Avatar' => __DIR__ . '/../../..' . '/lib/private/Avatar.php', 'OC\\AvatarManager' => __DIR__ . '/../../..' . '/lib/private/AvatarManager.php', 'OC\\BackgroundJob\\Job' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Job.php', 'OC\\BackgroundJob\\JobList' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/JobList.php', 'OC\\BackgroundJob\\Legacy\\QueuedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Legacy/QueuedJob.php', 'OC\\BackgroundJob\\Legacy\\RegularJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Legacy/RegularJob.php', 'OC\\BackgroundJob\\QueuedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/QueuedJob.php', 'OC\\BackgroundJob\\TimedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/TimedJob.php', 'OC\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/private/Cache/CappedMemoryCache.php', 'OC\\Cache\\File' => __DIR__ . '/../../..' . '/lib/private/Cache/File.php', 'OC\\CapabilitiesManager' => __DIR__ . '/../../..' . '/lib/private/CapabilitiesManager.php', 'OC\\Command\\AsyncBus' => __DIR__ . '/../../..' . '/lib/private/Command/AsyncBus.php', 'OC\\Command\\CallableJob' => __DIR__ . '/../../..' . '/lib/private/Command/CallableJob.php', 'OC\\Command\\ClosureJob' => __DIR__ . '/../../..' . '/lib/private/Command/ClosureJob.php', 'OC\\Command\\CommandJob' => __DIR__ . '/../../..' . '/lib/private/Command/CommandJob.php', 'OC\\Command\\FileAccess' => __DIR__ . '/../../..' . '/lib/private/Command/FileAccess.php', 'OC\\Command\\QueueBus' => __DIR__ . '/../../..' . '/lib/private/Command/QueueBus.php', 'OC\\Comments\\Comment' => __DIR__ . '/../../..' . '/lib/private/Comments/Comment.php', 'OC\\Comments\\Manager' => __DIR__ . '/../../..' . '/lib/private/Comments/Manager.php', 'OC\\Comments\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/Comments/ManagerFactory.php', 'OC\\Config' => __DIR__ . '/../../..' . '/lib/private/Config.php', 'OC\\Console\\Application' => __DIR__ . '/../../..' . '/lib/private/Console/Application.php', 'OC\\Console\\TimestampFormatter' => __DIR__ . '/../../..' . '/lib/private/Console/TimestampFormatter.php', 'OC\\ContactsManager' => __DIR__ . '/../../..' . '/lib/private/ContactsManager.php', 'OC\\Contacts\\ContactsMenu\\ActionFactory' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/ActionFactory.php', 'OC\\Contacts\\ContactsMenu\\ActionProviderStore' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/ActionProviderStore.php', 'OC\\Contacts\\ContactsMenu\\Actions\\LinkAction' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php', 'OC\\Contacts\\ContactsMenu\\ContactsStore' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/ContactsStore.php', 'OC\\Contacts\\ContactsMenu\\Entry' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Entry.php', 'OC\\Contacts\\ContactsMenu\\Manager' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Manager.php', 'OC\\Contacts\\ContactsMenu\\Providers\\EMailProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php', 'OC\\Core\\Application' => __DIR__ . '/../../..' . '/core/Application.php', 'OC\\Core\\Command\\App\\CheckCode' => __DIR__ . '/../../..' . '/core/Command/App/CheckCode.php', 'OC\\Core\\Command\\App\\Disable' => __DIR__ . '/../../..' . '/core/Command/App/Disable.php', 'OC\\Core\\Command\\App\\Enable' => __DIR__ . '/../../..' . '/core/Command/App/Enable.php', 'OC\\Core\\Command\\App\\GetPath' => __DIR__ . '/../../..' . '/core/Command/App/GetPath.php', 'OC\\Core\\Command\\App\\ListApps' => __DIR__ . '/../../..' . '/core/Command/App/ListApps.php', 'OC\\Core\\Command\\Background\\Ajax' => __DIR__ . '/../../..' . '/core/Command/Background/Ajax.php', 'OC\\Core\\Command\\Background\\Base' => __DIR__ . '/../../..' . '/core/Command/Background/Base.php', 'OC\\Core\\Command\\Background\\Cron' => __DIR__ . '/../../..' . '/core/Command/Background/Cron.php', 'OC\\Core\\Command\\Background\\WebCron' => __DIR__ . '/../../..' . '/core/Command/Background/WebCron.php', 'OC\\Core\\Command\\Base' => __DIR__ . '/../../..' . '/core/Command/Base.php', 'OC\\Core\\Command\\Check' => __DIR__ . '/../../..' . '/core/Command/Check.php', 'OC\\Core\\Command\\Config\\App\\Base' => __DIR__ . '/../../..' . '/core/Command/Config/App/Base.php', 'OC\\Core\\Command\\Config\\App\\DeleteConfig' => __DIR__ . '/../../..' . '/core/Command/Config/App/DeleteConfig.php', 'OC\\Core\\Command\\Config\\App\\GetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/App/GetConfig.php', 'OC\\Core\\Command\\Config\\App\\SetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/App/SetConfig.php', 'OC\\Core\\Command\\Config\\Import' => __DIR__ . '/../../..' . '/core/Command/Config/Import.php', 'OC\\Core\\Command\\Config\\ListConfigs' => __DIR__ . '/../../..' . '/core/Command/Config/ListConfigs.php', 'OC\\Core\\Command\\Config\\System\\Base' => __DIR__ . '/../../..' . '/core/Command/Config/System/Base.php', 'OC\\Core\\Command\\Config\\System\\DeleteConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/DeleteConfig.php', 'OC\\Core\\Command\\Config\\System\\GetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/GetConfig.php', 'OC\\Core\\Command\\Config\\System\\SetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/SetConfig.php', 'OC\\Core\\Command\\Db\\ConvertMysqlToMB4' => __DIR__ . '/../../..' . '/core/Command/Db/ConvertMysqlToMB4.php', 'OC\\Core\\Command\\Db\\ConvertType' => __DIR__ . '/../../..' . '/core/Command/Db/ConvertType.php', 'OC\\Core\\Command\\Db\\GenerateChangeScript' => __DIR__ . '/../../..' . '/core/Command/Db/GenerateChangeScript.php', 'OC\\Core\\Command\\Encryption\\ChangeKeyStorageRoot' => __DIR__ . '/../../..' . '/core/Command/Encryption/ChangeKeyStorageRoot.php', 'OC\\Core\\Command\\Encryption\\DecryptAll' => __DIR__ . '/../../..' . '/core/Command/Encryption/DecryptAll.php', 'OC\\Core\\Command\\Encryption\\Disable' => __DIR__ . '/../../..' . '/core/Command/Encryption/Disable.php', 'OC\\Core\\Command\\Encryption\\Enable' => __DIR__ . '/../../..' . '/core/Command/Encryption/Enable.php', 'OC\\Core\\Command\\Encryption\\EncryptAll' => __DIR__ . '/../../..' . '/core/Command/Encryption/EncryptAll.php', 'OC\\Core\\Command\\Encryption\\ListModules' => __DIR__ . '/../../..' . '/core/Command/Encryption/ListModules.php', 'OC\\Core\\Command\\Encryption\\SetDefaultModule' => __DIR__ . '/../../..' . '/core/Command/Encryption/SetDefaultModule.php', 'OC\\Core\\Command\\Encryption\\ShowKeyStorageRoot' => __DIR__ . '/../../..' . '/core/Command/Encryption/ShowKeyStorageRoot.php', 'OC\\Core\\Command\\Encryption\\Status' => __DIR__ . '/../../..' . '/core/Command/Encryption/Status.php', 'OC\\Core\\Command\\Group\\AddUser' => __DIR__ . '/../../..' . '/core/Command/Group/AddUser.php', 'OC\\Core\\Command\\Group\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/Group/ListCommand.php', 'OC\\Core\\Command\\Group\\RemoveUser' => __DIR__ . '/../../..' . '/core/Command/Group/RemoveUser.php', 'OC\\Core\\Command\\Integrity\\CheckApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckApp.php', 'OC\\Core\\Command\\Integrity\\CheckCore' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckCore.php', 'OC\\Core\\Command\\Integrity\\SignApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/SignApp.php', 'OC\\Core\\Command\\Integrity\\SignCore' => __DIR__ . '/../../..' . '/core/Command/Integrity/SignCore.php', 'OC\\Core\\Command\\InterruptedException' => __DIR__ . '/../../..' . '/core/Command/InterruptedException.php', 'OC\\Core\\Command\\L10n\\CreateJs' => __DIR__ . '/../../..' . '/core/Command/L10n/CreateJs.php', 'OC\\Core\\Command\\Log\\File' => __DIR__ . '/../../..' . '/core/Command/Log/File.php', 'OC\\Core\\Command\\Log\\Manage' => __DIR__ . '/../../..' . '/core/Command/Log/Manage.php', 'OC\\Core\\Command\\Maintenance\\DataFingerprint' => __DIR__ . '/../../..' . '/core/Command/Maintenance/DataFingerprint.php', 'OC\\Core\\Command\\Maintenance\\Install' => __DIR__ . '/../../..' . '/core/Command/Maintenance/Install.php', 'OC\\Core\\Command\\Maintenance\\Mimetype\\UpdateDB' => __DIR__ . '/../../..' . '/core/Command/Maintenance/Mimetype/UpdateDB.php', 'OC\\Core\\Command\\Maintenance\\Mimetype\\UpdateJS' => __DIR__ . '/../../..' . '/core/Command/Maintenance/Mimetype/UpdateJS.php', 'OC\\Core\\Command\\Maintenance\\Mode' => __DIR__ . '/../../..' . '/core/Command/Maintenance/Mode.php', 'OC\\Core\\Command\\Maintenance\\Repair' => __DIR__ . '/../../..' . '/core/Command/Maintenance/Repair.php', 'OC\\Core\\Command\\Maintenance\\UpdateHtaccess' => __DIR__ . '/../../..' . '/core/Command/Maintenance/UpdateHtaccess.php', 'OC\\Core\\Command\\Security\\ImportCertificate' => __DIR__ . '/../../..' . '/core/Command/Security/ImportCertificate.php', 'OC\\Core\\Command\\Security\\ListCertificates' => __DIR__ . '/../../..' . '/core/Command/Security/ListCertificates.php', 'OC\\Core\\Command\\Security\\RemoveCertificate' => __DIR__ . '/../../..' . '/core/Command/Security/RemoveCertificate.php', 'OC\\Core\\Command\\Status' => __DIR__ . '/../../..' . '/core/Command/Status.php', 'OC\\Core\\Command\\TwoFactorAuth\\Base' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Base.php', 'OC\\Core\\Command\\TwoFactorAuth\\Disable' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Disable.php', 'OC\\Core\\Command\\TwoFactorAuth\\Enable' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Enable.php', 'OC\\Core\\Command\\Upgrade' => __DIR__ . '/../../..' . '/core/Command/Upgrade.php', 'OC\\Core\\Command\\User\\Add' => __DIR__ . '/../../..' . '/core/Command/User/Add.php', 'OC\\Core\\Command\\User\\Delete' => __DIR__ . '/../../..' . '/core/Command/User/Delete.php', 'OC\\Core\\Command\\User\\Disable' => __DIR__ . '/../../..' . '/core/Command/User/Disable.php', 'OC\\Core\\Command\\User\\Enable' => __DIR__ . '/../../..' . '/core/Command/User/Enable.php', 'OC\\Core\\Command\\User\\Info' => __DIR__ . '/../../..' . '/core/Command/User/Info.php', 'OC\\Core\\Command\\User\\LastSeen' => __DIR__ . '/../../..' . '/core/Command/User/LastSeen.php', 'OC\\Core\\Command\\User\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/User/ListCommand.php', 'OC\\Core\\Command\\User\\Report' => __DIR__ . '/../../..' . '/core/Command/User/Report.php', 'OC\\Core\\Command\\User\\ResetPassword' => __DIR__ . '/../../..' . '/core/Command/User/ResetPassword.php', 'OC\\Core\\Command\\User\\Setting' => __DIR__ . '/../../..' . '/core/Command/User/Setting.php', 'OC\\Core\\Controller\\AvatarController' => __DIR__ . '/../../..' . '/core/Controller/AvatarController.php', 'OC\\Core\\Controller\\ClientFlowLoginController' => __DIR__ . '/../../..' . '/core/Controller/ClientFlowLoginController.php', 'OC\\Core\\Controller\\ContactsMenuController' => __DIR__ . '/../../..' . '/core/Controller/ContactsMenuController.php', 'OC\\Core\\Controller\\CssController' => __DIR__ . '/../../..' . '/core/Controller/CssController.php', 'OC\\Core\\Controller\\JsController' => __DIR__ . '/../../..' . '/core/Controller/JsController.php', 'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\OCJSController' => __DIR__ . '/../../..' . '/core/Controller/OCJSController.php', 'OC\\Core\\Controller\\OCSController' => __DIR__ . '/../../..' . '/core/Controller/OCSController.php', 'OC\\Core\\Controller\\PreviewController' => __DIR__ . '/../../..' . '/core/Controller/PreviewController.php', 'OC\\Core\\Controller\\SetupController' => __DIR__ . '/../../..' . '/core/Controller/SetupController.php', 'OC\\Core\\Controller\\TwoFactorChallengeController' => __DIR__ . '/../../..' . '/core/Controller/TwoFactorChallengeController.php', 'OC\\Core\\Controller\\UserController' => __DIR__ . '/../../..' . '/core/Controller/UserController.php', 'OC\\Core\\Middleware\\TwoFactorMiddleware' => __DIR__ . '/../../..' . '/core/Middleware/TwoFactorMiddleware.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', 'OC\\DB\\AdapterMySQL' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterMySQL.php', 'OC\\DB\\AdapterOCI8' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterOCI8.php', 'OC\\DB\\AdapterPgSql' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterPgSql.php', 'OC\\DB\\AdapterSqlite' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterSqlite.php', 'OC\\DB\\Connection' => __DIR__ . '/../../..' . '/lib/private/DB/Connection.php', 'OC\\DB\\ConnectionFactory' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionFactory.php', 'OC\\DB\\MDB2SchemaManager' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaManager.php', 'OC\\DB\\MDB2SchemaReader' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaReader.php', 'OC\\DB\\MDB2SchemaWriter' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaWriter.php', 'OC\\DB\\MigrationException' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationException.php', 'OC\\DB\\Migrator' => __DIR__ . '/../../..' . '/lib/private/DB/Migrator.php', 'OC\\DB\\MySQLMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/MySQLMigrator.php', 'OC\\DB\\MySqlTools' => __DIR__ . '/../../..' . '/lib/private/DB/MySqlTools.php', 'OC\\DB\\NoCheckMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/NoCheckMigrator.php', 'OC\\DB\\OCSqlitePlatform' => __DIR__ . '/../../..' . '/lib/private/DB/OCSqlitePlatform.php', 'OC\\DB\\OracleConnection' => __DIR__ . '/../../..' . '/lib/private/DB/OracleConnection.php', 'OC\\DB\\OracleMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/OracleMigrator.php', 'OC\\DB\\PgSqlTools' => __DIR__ . '/../../..' . '/lib/private/DB/PgSqlTools.php', 'OC\\DB\\PostgreSqlMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/PostgreSqlMigrator.php', 'OC\\DB\\QueryBuilder\\CompositeExpression' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/CompositeExpression.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\ExpressionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\MySqlExpressionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\OCIExpressionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\PgSqlExpressionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\ExpressionBuilder\\SqliteExpressionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php', 'OC\\DB\\QueryBuilder\\FunctionBuilder\\FunctionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php', 'OC\\DB\\QueryBuilder\\FunctionBuilder\\OCIFunctionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php', 'OC\\DB\\QueryBuilder\\FunctionBuilder\\PgSqlFunctionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php', 'OC\\DB\\QueryBuilder\\FunctionBuilder\\SqliteFunctionBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php', 'OC\\DB\\QueryBuilder\\Literal' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/Literal.php', 'OC\\DB\\QueryBuilder\\Parameter' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/Parameter.php', 'OC\\DB\\QueryBuilder\\QueryBuilder' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/QueryBuilder.php', 'OC\\DB\\QueryBuilder\\QueryFunction' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/QueryFunction.php', 'OC\\DB\\QueryBuilder\\QuoteHelper' => __DIR__ . '/../../..' . '/lib/private/DB/QueryBuilder/QuoteHelper.php', 'OC\\DB\\SQLiteMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/SQLiteMigrator.php', 'OC\\DB\\SQLiteSessionInit' => __DIR__ . '/../../..' . '/lib/private/DB/SQLiteSessionInit.php', 'OC\\DatabaseException' => __DIR__ . '/../../..' . '/lib/private/DatabaseException.php', 'OC\\DatabaseSetupException' => __DIR__ . '/../../..' . '/lib/private/DatabaseSetupException.php', 'OC\\DateTimeFormatter' => __DIR__ . '/../../..' . '/lib/private/DateTimeFormatter.php', 'OC\\DateTimeZone' => __DIR__ . '/../../..' . '/lib/private/DateTimeZone.php', 'OC\\Diagnostics\\Event' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/Event.php', 'OC\\Diagnostics\\EventLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/EventLogger.php', 'OC\\Diagnostics\\Query' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/Query.php', 'OC\\Diagnostics\\QueryLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/QueryLogger.php', 'OC\\Encryption\\DecryptAll' => __DIR__ . '/../../..' . '/lib/private/Encryption/DecryptAll.php', 'OC\\Encryption\\EncryptionWrapper' => __DIR__ . '/../../..' . '/lib/private/Encryption/EncryptionWrapper.php', 'OC\\Encryption\\Exceptions\\DecryptionFailedException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/DecryptionFailedException.php', 'OC\\Encryption\\Exceptions\\EmptyEncryptionDataException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php', 'OC\\Encryption\\Exceptions\\EncryptionFailedException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/EncryptionFailedException.php', 'OC\\Encryption\\Exceptions\\EncryptionHeaderKeyExistsException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php', 'OC\\Encryption\\Exceptions\\EncryptionHeaderToLargeException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php', 'OC\\Encryption\\Exceptions\\ModuleAlreadyExistsException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php', 'OC\\Encryption\\Exceptions\\ModuleDoesNotExistsException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php', 'OC\\Encryption\\Exceptions\\UnknownCipherException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/UnknownCipherException.php', 'OC\\Encryption\\File' => __DIR__ . '/../../..' . '/lib/private/Encryption/File.php', 'OC\\Encryption\\HookManager' => __DIR__ . '/../../..' . '/lib/private/Encryption/HookManager.php', 'OC\\Encryption\\Keys\\Storage' => __DIR__ . '/../../..' . '/lib/private/Encryption/Keys/Storage.php', 'OC\\Encryption\\Manager' => __DIR__ . '/../../..' . '/lib/private/Encryption/Manager.php', 'OC\\Encryption\\Update' => __DIR__ . '/../../..' . '/lib/private/Encryption/Update.php', 'OC\\Encryption\\Util' => __DIR__ . '/../../..' . '/lib/private/Encryption/Util.php', 'OC\\Federation\\CloudId' => __DIR__ . '/../../..' . '/lib/private/Federation/CloudId.php', 'OC\\Federation\\CloudIdManager' => __DIR__ . '/../../..' . '/lib/private/Federation/CloudIdManager.php', 'OC\\Files\\AppData\\AppData' => __DIR__ . '/../../..' . '/lib/private/Files/AppData/AppData.php', 'OC\\Files\\AppData\\Factory' => __DIR__ . '/../../..' . '/lib/private/Files/AppData/Factory.php', 'OC\\Files\\Cache\\Cache' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Cache.php', 'OC\\Files\\Cache\\CacheEntry' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/CacheEntry.php', 'OC\\Files\\Cache\\FailedCache' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/FailedCache.php', 'OC\\Files\\Cache\\HomeCache' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/HomeCache.php', 'OC\\Files\\Cache\\HomePropagator' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/HomePropagator.php', 'OC\\Files\\Cache\\MoveFromCacheTrait' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/MoveFromCacheTrait.php', 'OC\\Files\\Cache\\Propagator' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Propagator.php', 'OC\\Files\\Cache\\QuerySearchHelper' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/QuerySearchHelper.php', 'OC\\Files\\Cache\\Scanner' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Scanner.php', 'OC\\Files\\Cache\\Storage' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Storage.php', 'OC\\Files\\Cache\\StorageGlobal' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/StorageGlobal.php', 'OC\\Files\\Cache\\Updater' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Updater.php', 'OC\\Files\\Cache\\Watcher' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Watcher.php', 'OC\\Files\\Cache\\Wrapper\\CacheJail' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CacheJail.php', 'OC\\Files\\Cache\\Wrapper\\CachePermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php', 'OC\\Files\\Cache\\Wrapper\\CacheWrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CacheWrapper.php', 'OC\\Files\\Cache\\Wrapper\\JailPropagator' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/JailPropagator.php', 'OC\\Files\\Config\\CachedMountInfo' => __DIR__ . '/../../..' . '/lib/private/Files/Config/CachedMountInfo.php', 'OC\\Files\\Config\\LazyStorageMountInfo' => __DIR__ . '/../../..' . '/lib/private/Files/Config/LazyStorageMountInfo.php', 'OC\\Files\\Config\\MountProviderCollection' => __DIR__ . '/../../..' . '/lib/private/Files/Config/MountProviderCollection.php', 'OC\\Files\\Config\\UserMountCache' => __DIR__ . '/../../..' . '/lib/private/Files/Config/UserMountCache.php', 'OC\\Files\\Config\\UserMountCacheListener' => __DIR__ . '/../../..' . '/lib/private/Files/Config/UserMountCacheListener.php', 'OC\\Files\\FileInfo' => __DIR__ . '/../../..' . '/lib/private/Files/FileInfo.php', 'OC\\Files\\Filesystem' => __DIR__ . '/../../..' . '/lib/private/Files/Filesystem.php', 'OC\\Files\\Mount\\CacheMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/CacheMountProvider.php', 'OC\\Files\\Mount\\LocalHomeMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/LocalHomeMountProvider.php', 'OC\\Files\\Mount\\Manager' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/Manager.php', 'OC\\Files\\Mount\\MountPoint' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/MountPoint.php', 'OC\\Files\\Mount\\MoveableMount' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/MoveableMount.php', 'OC\\Files\\Mount\\ObjectHomeMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/ObjectHomeMountProvider.php', 'OC\\Files\\Node\\File' => __DIR__ . '/../../..' . '/lib/private/Files/Node/File.php', 'OC\\Files\\Node\\Folder' => __DIR__ . '/../../..' . '/lib/private/Files/Node/Folder.php', 'OC\\Files\\Node\\HookConnector' => __DIR__ . '/../../..' . '/lib/private/Files/Node/HookConnector.php', 'OC\\Files\\Node\\LazyRoot' => __DIR__ . '/../../..' . '/lib/private/Files/Node/LazyRoot.php', 'OC\\Files\\Node\\Node' => __DIR__ . '/../../..' . '/lib/private/Files/Node/Node.php', 'OC\\Files\\Node\\NonExistingFile' => __DIR__ . '/../../..' . '/lib/private/Files/Node/NonExistingFile.php', 'OC\\Files\\Node\\NonExistingFolder' => __DIR__ . '/../../..' . '/lib/private/Files/Node/NonExistingFolder.php', 'OC\\Files\\Node\\Root' => __DIR__ . '/../../..' . '/lib/private/Files/Node/Root.php', 'OC\\Files\\Notify\\Change' => __DIR__ . '/../../..' . '/lib/private/Files/Notify/Change.php', 'OC\\Files\\Notify\\RenameChange' => __DIR__ . '/../../..' . '/lib/private/Files/Notify/RenameChange.php', 'OC\\Files\\ObjectStore\\HomeObjectStoreStorage' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\Mapper' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/Mapper.php', 'OC\\Files\\ObjectStore\\NoopScanner' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/NoopScanner.php', 'OC\\Files\\ObjectStore\\ObjectStoreStorage' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/ObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\S3' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/S3.php', 'OC\\Files\\ObjectStore\\S3ConnectionTrait' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/S3ConnectionTrait.php', 'OC\\Files\\ObjectStore\\StorageObjectStore' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/StorageObjectStore.php', 'OC\\Files\\ObjectStore\\Swift' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/Swift.php', 'OC\\Files\\Search\\SearchBinaryOperator' => __DIR__ . '/../../..' . '/lib/private/Files/Search/SearchBinaryOperator.php', 'OC\\Files\\Search\\SearchComparison' => __DIR__ . '/../../..' . '/lib/private/Files/Search/SearchComparison.php', 'OC\\Files\\Search\\SearchOrder' => __DIR__ . '/../../..' . '/lib/private/Files/Search/SearchOrder.php', 'OC\\Files\\Search\\SearchQuery' => __DIR__ . '/../../..' . '/lib/private/Files/Search/SearchQuery.php', 'OC\\Files\\SimpleFS\\SimpleFile' => __DIR__ . '/../../..' . '/lib/private/Files/SimpleFS/SimpleFile.php', 'OC\\Files\\SimpleFS\\SimpleFolder' => __DIR__ . '/../../..' . '/lib/private/Files/SimpleFS/SimpleFolder.php', 'OC\\Files\\Storage\\Common' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Common.php', 'OC\\Files\\Storage\\CommonTest' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/CommonTest.php', 'OC\\Files\\Storage\\DAV' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/DAV.php', 'OC\\Files\\Storage\\FailedStorage' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/FailedStorage.php', 'OC\\Files\\Storage\\Flysystem' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Flysystem.php', 'OC\\Files\\Storage\\Home' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Home.php', 'OC\\Files\\Storage\\Local' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Local.php', 'OC\\Files\\Storage\\LocalTempFileTrait' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/LocalTempFileTrait.php', 'OC\\Files\\Storage\\PolyFill\\CopyDirectory' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/PolyFill/CopyDirectory.php', 'OC\\Files\\Storage\\Storage' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Storage.php', 'OC\\Files\\Storage\\StorageFactory' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/StorageFactory.php', 'OC\\Files\\Storage\\Temporary' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Temporary.php', 'OC\\Files\\Storage\\Wrapper\\Availability' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Availability.php', 'OC\\Files\\Storage\\Wrapper\\Encoding' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Encoding.php', 'OC\\Files\\Storage\\Wrapper\\Encryption' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Encryption.php', 'OC\\Files\\Storage\\Wrapper\\Jail' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Jail.php', 'OC\\Files\\Storage\\Wrapper\\PermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/PermissionsMask.php', 'OC\\Files\\Storage\\Wrapper\\Quota' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Quota.php', 'OC\\Files\\Storage\\Wrapper\\Wrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Wrapper.php', 'OC\\Files\\Stream\\Encryption' => __DIR__ . '/../../..' . '/lib/private/Files/Stream/Encryption.php', 'OC\\Files\\Stream\\Quota' => __DIR__ . '/../../..' . '/lib/private/Files/Stream/Quota.php', 'OC\\Files\\Type\\Detection' => __DIR__ . '/../../..' . '/lib/private/Files/Type/Detection.php', 'OC\\Files\\Type\\Loader' => __DIR__ . '/../../..' . '/lib/private/Files/Type/Loader.php', 'OC\\Files\\Type\\TemplateManager' => __DIR__ . '/../../..' . '/lib/private/Files/Type/TemplateManager.php', 'OC\\Files\\Utils\\Scanner' => __DIR__ . '/../../..' . '/lib/private/Files/Utils/Scanner.php', 'OC\\Files\\View' => __DIR__ . '/../../..' . '/lib/private/Files/View.php', 'OC\\ForbiddenException' => __DIR__ . '/../../..' . '/lib/private/ForbiddenException.php', 'OC\\GlobalScale\\Config' => __DIR__ . '/../../..' . '/lib/private/GlobalScale/Config.php', 'OC\\Group\\Backend' => __DIR__ . '/../../..' . '/lib/private/Group/Backend.php', 'OC\\Group\\Database' => __DIR__ . '/../../..' . '/lib/private/Group/Database.php', 'OC\\Group\\Group' => __DIR__ . '/../../..' . '/lib/private/Group/Group.php', 'OC\\Group\\Manager' => __DIR__ . '/../../..' . '/lib/private/Group/Manager.php', 'OC\\Group\\MetaData' => __DIR__ . '/../../..' . '/lib/private/Group/MetaData.php', 'OC\\HTTPHelper' => __DIR__ . '/../../..' . '/lib/private/HTTPHelper.php', 'OC\\HintException' => __DIR__ . '/../../..' . '/lib/private/HintException.php', 'OC\\Hooks\\BasicEmitter' => __DIR__ . '/../../..' . '/lib/private/Hooks/BasicEmitter.php', 'OC\\Hooks\\Emitter' => __DIR__ . '/../../..' . '/lib/private/Hooks/Emitter.php', 'OC\\Hooks\\EmitterTrait' => __DIR__ . '/../../..' . '/lib/private/Hooks/EmitterTrait.php', 'OC\\Hooks\\ForwardingEmitter' => __DIR__ . '/../../..' . '/lib/private/Hooks/ForwardingEmitter.php', 'OC\\Hooks\\LegacyEmitter' => __DIR__ . '/../../..' . '/lib/private/Hooks/LegacyEmitter.php', 'OC\\Hooks\\PublicEmitter' => __DIR__ . '/../../..' . '/lib/private/Hooks/PublicEmitter.php', 'OC\\Http\\Client\\Client' => __DIR__ . '/../../..' . '/lib/private/Http/Client/Client.php', 'OC\\Http\\Client\\ClientService' => __DIR__ . '/../../..' . '/lib/private/Http/Client/ClientService.php', 'OC\\Http\\Client\\Response' => __DIR__ . '/../../..' . '/lib/private/Http/Client/Response.php', 'OC\\Installer' => __DIR__ . '/../../..' . '/lib/private/Installer.php', 'OC\\IntegrityCheck\\Checker' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Checker.php', 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php', 'OC\\IntegrityCheck\\Helpers\\AppLocator' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Helpers/AppLocator.php', 'OC\\IntegrityCheck\\Helpers\\EnvironmentHelper' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Helpers/EnvironmentHelper.php', 'OC\\IntegrityCheck\\Helpers\\FileAccessHelper' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php', 'OC\\IntegrityCheck\\Iterator\\ExcludeFileByNameFilterIterator' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php', 'OC\\IntegrityCheck\\Iterator\\ExcludeFoldersByPathFilterIterator' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php', 'OC\\L10N\\Factory' => __DIR__ . '/../../..' . '/lib/private/L10N/Factory.php', 'OC\\L10N\\L10N' => __DIR__ . '/../../..' . '/lib/private/L10N/L10N.php', 'OC\\L10N\\LanguageNotFoundException' => __DIR__ . '/../../..' . '/lib/private/L10N/LanguageNotFoundException.php', 'OC\\LargeFileHelper' => __DIR__ . '/../../..' . '/lib/private/LargeFileHelper.php', 'OC\\Lock\\AbstractLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/AbstractLockingProvider.php', 'OC\\Lock\\DBLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/DBLockingProvider.php', 'OC\\Lock\\MemcacheLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/MemcacheLockingProvider.php', 'OC\\Lock\\NoopLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/NoopLockingProvider.php', 'OC\\Lockdown\\Filesystem\\NullCache' => __DIR__ . '/../../..' . '/lib/private/Lockdown/Filesystem/NullCache.php', 'OC\\Lockdown\\Filesystem\\NullStorage' => __DIR__ . '/../../..' . '/lib/private/Lockdown/Filesystem/NullStorage.php', 'OC\\Lockdown\\LockdownManager' => __DIR__ . '/../../..' . '/lib/private/Lockdown/LockdownManager.php', 'OC\\Log' => __DIR__ . '/../../..' . '/lib/private/Log.php', 'OC\\Log\\ErrorHandler' => __DIR__ . '/../../..' . '/lib/private/Log/ErrorHandler.php', 'OC\\Log\\Errorlog' => __DIR__ . '/../../..' . '/lib/private/Log/Errorlog.php', 'OC\\Log\\File' => __DIR__ . '/../../..' . '/lib/private/Log/File.php', 'OC\\Log\\Rotate' => __DIR__ . '/../../..' . '/lib/private/Log/Rotate.php', 'OC\\Log\\Syslog' => __DIR__ . '/../../..' . '/lib/private/Log/Syslog.php', 'OC\\Mail\\EMailTemplate' => __DIR__ . '/../../..' . '/lib/private/Mail/EMailTemplate.php', 'OC\\Mail\\Mailer' => __DIR__ . '/../../..' . '/lib/private/Mail/Mailer.php', 'OC\\Mail\\Message' => __DIR__ . '/../../..' . '/lib/private/Mail/Message.php', 'OC\\Memcache\\APCu' => __DIR__ . '/../../..' . '/lib/private/Memcache/APCu.php', 'OC\\Memcache\\ArrayCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/ArrayCache.php', 'OC\\Memcache\\CADTrait' => __DIR__ . '/../../..' . '/lib/private/Memcache/CADTrait.php', 'OC\\Memcache\\CASTrait' => __DIR__ . '/../../..' . '/lib/private/Memcache/CASTrait.php', 'OC\\Memcache\\Cache' => __DIR__ . '/../../..' . '/lib/private/Memcache/Cache.php', 'OC\\Memcache\\Factory' => __DIR__ . '/../../..' . '/lib/private/Memcache/Factory.php', 'OC\\Memcache\\Memcached' => __DIR__ . '/../../..' . '/lib/private/Memcache/Memcached.php', 'OC\\Memcache\\NullCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/NullCache.php', 'OC\\Memcache\\Redis' => __DIR__ . '/../../..' . '/lib/private/Memcache/Redis.php', 'OC\\Memcache\\XCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/XCache.php', 'OC\\Migration\\BackgroundRepair' => __DIR__ . '/../../..' . '/lib/private/Migration/BackgroundRepair.php', 'OC\\Migration\\ConsoleOutput' => __DIR__ . '/../../..' . '/lib/private/Migration/ConsoleOutput.php', 'OC\\NaturalSort' => __DIR__ . '/../../..' . '/lib/private/NaturalSort.php', 'OC\\NaturalSort_DefaultCollator' => __DIR__ . '/../../..' . '/lib/private/NaturalSort_DefaultCollator.php', 'OC\\NavigationManager' => __DIR__ . '/../../..' . '/lib/private/NavigationManager.php', 'OC\\NeedsUpdateException' => __DIR__ . '/../../..' . '/lib/private/NeedsUpdateException.php', 'OC\\NotSquareException' => __DIR__ . '/../../..' . '/lib/private/NotSquareException.php', 'OC\\Notification\\Action' => __DIR__ . '/../../..' . '/lib/private/Notification/Action.php', 'OC\\Notification\\Manager' => __DIR__ . '/../../..' . '/lib/private/Notification/Manager.php', 'OC\\Notification\\Notification' => __DIR__ . '/../../..' . '/lib/private/Notification/Notification.php', 'OC\\OCS\\CoreCapabilities' => __DIR__ . '/../../..' . '/lib/private/OCS/CoreCapabilities.php', 'OC\\OCS\\DiscoveryService' => __DIR__ . '/../../..' . '/lib/private/OCS/DiscoveryService.php', 'OC\\OCS\\Exception' => __DIR__ . '/../../..' . '/lib/private/OCS/Exception.php', 'OC\\OCS\\PrivateData' => __DIR__ . '/../../..' . '/lib/private/OCS/PrivateData.php', 'OC\\OCS\\Provider' => __DIR__ . '/../../..' . '/lib/private/OCS/Provider.php', 'OC\\OCS\\Result' => __DIR__ . '/../../..' . '/lib/private/OCS/Result.php', 'OC\\PreviewManager' => __DIR__ . '/../../..' . '/lib/private/PreviewManager.php', 'OC\\PreviewNotAvailableException' => __DIR__ . '/../../..' . '/lib/private/PreviewNotAvailableException.php', 'OC\\Preview\\BMP' => __DIR__ . '/../../..' . '/lib/private/Preview/BMP.php', 'OC\\Preview\\Bitmap' => __DIR__ . '/../../..' . '/lib/private/Preview/Bitmap.php', 'OC\\Preview\\Font' => __DIR__ . '/../../..' . '/lib/private/Preview/Font.php', 'OC\\Preview\\GIF' => __DIR__ . '/../../..' . '/lib/private/Preview/GIF.php', 'OC\\Preview\\Generator' => __DIR__ . '/../../..' . '/lib/private/Preview/Generator.php', 'OC\\Preview\\GeneratorHelper' => __DIR__ . '/../../..' . '/lib/private/Preview/GeneratorHelper.php', 'OC\\Preview\\Illustrator' => __DIR__ . '/../../..' . '/lib/private/Preview/Illustrator.php', 'OC\\Preview\\Image' => __DIR__ . '/../../..' . '/lib/private/Preview/Image.php', 'OC\\Preview\\JPEG' => __DIR__ . '/../../..' . '/lib/private/Preview/JPEG.php', 'OC\\Preview\\MP3' => __DIR__ . '/../../..' . '/lib/private/Preview/MP3.php', 'OC\\Preview\\MSOffice2003' => __DIR__ . '/../../..' . '/lib/private/Preview/MSOffice2003.php', 'OC\\Preview\\MSOffice2007' => __DIR__ . '/../../..' . '/lib/private/Preview/MSOffice2007.php', 'OC\\Preview\\MSOfficeDoc' => __DIR__ . '/../../..' . '/lib/private/Preview/MSOfficeDoc.php', 'OC\\Preview\\MarkDown' => __DIR__ . '/../../..' . '/lib/private/Preview/MarkDown.php', 'OC\\Preview\\Movie' => __DIR__ . '/../../..' . '/lib/private/Preview/Movie.php', 'OC\\Preview\\Office' => __DIR__ . '/../../..' . '/lib/private/Preview/Office.php', 'OC\\Preview\\OpenDocument' => __DIR__ . '/../../..' . '/lib/private/Preview/OpenDocument.php', 'OC\\Preview\\PDF' => __DIR__ . '/../../..' . '/lib/private/Preview/PDF.php', 'OC\\Preview\\PNG' => __DIR__ . '/../../..' . '/lib/private/Preview/PNG.php', 'OC\\Preview\\Photoshop' => __DIR__ . '/../../..' . '/lib/private/Preview/Photoshop.php', 'OC\\Preview\\Postscript' => __DIR__ . '/../../..' . '/lib/private/Preview/Postscript.php', 'OC\\Preview\\Provider' => __DIR__ . '/../../..' . '/lib/private/Preview/Provider.php', 'OC\\Preview\\SVG' => __DIR__ . '/../../..' . '/lib/private/Preview/SVG.php', 'OC\\Preview\\StarOffice' => __DIR__ . '/../../..' . '/lib/private/Preview/StarOffice.php', 'OC\\Preview\\TIFF' => __DIR__ . '/../../..' . '/lib/private/Preview/TIFF.php', 'OC\\Preview\\TXT' => __DIR__ . '/../../..' . '/lib/private/Preview/TXT.php', 'OC\\Preview\\Watcher' => __DIR__ . '/../../..' . '/lib/private/Preview/Watcher.php', 'OC\\Preview\\WatcherConnector' => __DIR__ . '/../../..' . '/lib/private/Preview/WatcherConnector.php', 'OC\\Preview\\XBitmap' => __DIR__ . '/../../..' . '/lib/private/Preview/XBitmap.php', 'OC\\RedisFactory' => __DIR__ . '/../../..' . '/lib/private/RedisFactory.php', 'OC\\Repair' => __DIR__ . '/../../..' . '/lib/private/Repair.php', 'OC\\RepairException' => __DIR__ . '/../../..' . '/lib/private/RepairException.php', 'OC\\Repair\\CleanTags' => __DIR__ . '/../../..' . '/lib/private/Repair/CleanTags.php', 'OC\\Repair\\Collation' => __DIR__ . '/../../..' . '/lib/private/Repair/Collation.php', 'OC\\Repair\\MoveUpdaterStepFile' => __DIR__ . '/../../..' . '/lib/private/Repair/MoveUpdaterStepFile.php', 'OC\\Repair\\NC11\\CleanPreviews' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/CleanPreviews.php', 'OC\\Repair\\NC11\\CleanPreviewsBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/CleanPreviewsBackgroundJob.php', 'OC\\Repair\\NC11\\FixMountStorages' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/FixMountStorages.php', 'OC\\Repair\\NC11\\MoveAvatars' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/MoveAvatars.php', 'OC\\Repair\\NC11\\MoveAvatarsBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php', 'OC\\Repair\\NC12\\InstallCoreBundle' => __DIR__ . '/../../..' . '/lib/private/Repair/NC12/InstallCoreBundle.php', 'OC\\Repair\\NC12\\RepairIdentityProofKeyFolders' => __DIR__ . '/../../..' . '/lib/private/Repair/NC12/RepairIdentityProofKeyFolders.php', 'OC\\Repair\\NC12\\UpdateLanguageCodes' => __DIR__ . '/../../..' . '/lib/private/Repair/NC12/UpdateLanguageCodes.php', 'OC\\Repair\\NC13\\RepairInvalidPaths' => __DIR__ . '/../../..' . '/lib/private/Repair/NC13/RepairInvalidPaths.php', 'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php', 'OC\\Repair\\Owncloud\\DropAccountTermsTable' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php', 'OC\\Repair\\Owncloud\\SaveAccountsTableData' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php', 'OC\\Repair\\RemoveRootShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveRootShares.php', 'OC\\Repair\\RepairInvalidShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairInvalidShares.php', 'OC\\Repair\\RepairMimeTypes' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairMimeTypes.php', 'OC\\Repair\\SqliteAutoincrement' => __DIR__ . '/../../..' . '/lib/private/Repair/SqliteAutoincrement.php', 'OC\\RichObjectStrings\\Validator' => __DIR__ . '/../../..' . '/lib/private/RichObjectStrings/Validator.php', 'OC\\Route\\CachingRouter' => __DIR__ . '/../../..' . '/lib/private/Route/CachingRouter.php', 'OC\\Route\\Route' => __DIR__ . '/../../..' . '/lib/private/Route/Route.php', 'OC\\Route\\Router' => __DIR__ . '/../../..' . '/lib/private/Route/Router.php', 'OC\\Search' => __DIR__ . '/../../..' . '/lib/private/Search.php', 'OC\\Search\\Provider\\File' => __DIR__ . '/../../..' . '/lib/private/Search/Provider/File.php', 'OC\\Search\\Result\\Audio' => __DIR__ . '/../../..' . '/lib/private/Search/Result/Audio.php', 'OC\\Search\\Result\\File' => __DIR__ . '/../../..' . '/lib/private/Search/Result/File.php', 'OC\\Search\\Result\\Folder' => __DIR__ . '/../../..' . '/lib/private/Search/Result/Folder.php', 'OC\\Search\\Result\\Image' => __DIR__ . '/../../..' . '/lib/private/Search/Result/Image.php', 'OC\\Security\\Bruteforce\\Throttler' => __DIR__ . '/../../..' . '/lib/private/Security/Bruteforce/Throttler.php', 'OC\\Security\\CSP\\ContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/private/Security/CSP/ContentSecurityPolicy.php', 'OC\\Security\\CSP\\ContentSecurityPolicyManager' => __DIR__ . '/../../..' . '/lib/private/Security/CSP/ContentSecurityPolicyManager.php', 'OC\\Security\\CSP\\ContentSecurityPolicyNonceManager' => __DIR__ . '/../../..' . '/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php', 'OC\\Security\\CSRF\\CsrfToken' => __DIR__ . '/../../..' . '/lib/private/Security/CSRF/CsrfToken.php', 'OC\\Security\\CSRF\\CsrfTokenGenerator' => __DIR__ . '/../../..' . '/lib/private/Security/CSRF/CsrfTokenGenerator.php', 'OC\\Security\\CSRF\\CsrfTokenManager' => __DIR__ . '/../../..' . '/lib/private/Security/CSRF/CsrfTokenManager.php', 'OC\\Security\\CSRF\\TokenStorage\\SessionStorage' => __DIR__ . '/../../..' . '/lib/private/Security/CSRF/TokenStorage/SessionStorage.php', 'OC\\Security\\Certificate' => __DIR__ . '/../../..' . '/lib/private/Security/Certificate.php', 'OC\\Security\\CertificateManager' => __DIR__ . '/../../..' . '/lib/private/Security/CertificateManager.php', 'OC\\Security\\CredentialsManager' => __DIR__ . '/../../..' . '/lib/private/Security/CredentialsManager.php', 'OC\\Security\\Crypto' => __DIR__ . '/../../..' . '/lib/private/Security/Crypto.php', 'OC\\Security\\Hasher' => __DIR__ . '/../../..' . '/lib/private/Security/Hasher.php', 'OC\\Security\\IdentityProof\\Key' => __DIR__ . '/../../..' . '/lib/private/Security/IdentityProof/Key.php', 'OC\\Security\\IdentityProof\\Manager' => __DIR__ . '/../../..' . '/lib/private/Security/IdentityProof/Manager.php', 'OC\\Security\\IdentityProof\\Signer' => __DIR__ . '/../../..' . '/lib/private/Security/IdentityProof/Signer.php', 'OC\\Security\\Normalizer\\IpAddress' => __DIR__ . '/../../..' . '/lib/private/Security/Normalizer/IpAddress.php', 'OC\\Security\\RateLimiting\\Backend\\IBackend' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Backend/IBackend.php', 'OC\\Security\\RateLimiting\\Backend\\MemoryCache' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Backend/MemoryCache.php', 'OC\\Security\\RateLimiting\\Exception\\RateLimitExceededException' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Exception/RateLimitExceededException.php', 'OC\\Security\\RateLimiting\\Limiter' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Limiter.php', 'OC\\Security\\SecureRandom' => __DIR__ . '/../../..' . '/lib/private/Security/SecureRandom.php', 'OC\\Security\\TrustedDomainHelper' => __DIR__ . '/../../..' . '/lib/private/Security/TrustedDomainHelper.php', 'OC\\Server' => __DIR__ . '/../../..' . '/lib/private/Server.php', 'OC\\ServerContainer' => __DIR__ . '/../../..' . '/lib/private/ServerContainer.php', 'OC\\ServerNotAvailableException' => __DIR__ . '/../../..' . '/lib/private/ServerNotAvailableException.php', 'OC\\ServiceUnavailableException' => __DIR__ . '/../../..' . '/lib/private/ServiceUnavailableException.php', 'OC\\Session\\CryptoSessionData' => __DIR__ . '/../../..' . '/lib/private/Session/CryptoSessionData.php', 'OC\\Session\\CryptoWrapper' => __DIR__ . '/../../..' . '/lib/private/Session/CryptoWrapper.php', 'OC\\Session\\Internal' => __DIR__ . '/../../..' . '/lib/private/Session/Internal.php', 'OC\\Session\\Memory' => __DIR__ . '/../../..' . '/lib/private/Session/Memory.php', 'OC\\Session\\Session' => __DIR__ . '/../../..' . '/lib/private/Session/Session.php', 'OC\\Settings\\Activity\\Provider' => __DIR__ . '/../../..' . '/settings/Activity/Provider.php', 'OC\\Settings\\Activity\\SecurityFilter' => __DIR__ . '/../../..' . '/settings/Activity/SecurityFilter.php', 'OC\\Settings\\Activity\\SecurityProvider' => __DIR__ . '/../../..' . '/settings/Activity/SecurityProvider.php', 'OC\\Settings\\Activity\\SecuritySetting' => __DIR__ . '/../../..' . '/settings/Activity/SecuritySetting.php', 'OC\\Settings\\Activity\\Setting' => __DIR__ . '/../../..' . '/settings/Activity/Setting.php', 'OC\\Settings\\Admin\\Additional' => __DIR__ . '/../../..' . '/lib/private/Settings/Admin/Additional.php', 'OC\\Settings\\Admin\\Encryption' => __DIR__ . '/../../..' . '/lib/private/Settings/Admin/Encryption.php', 'OC\\Settings\\Admin\\Server' => __DIR__ . '/../../..' . '/lib/private/Settings/Admin/Server.php', 'OC\\Settings\\Admin\\ServerDevNotice' => __DIR__ . '/../../..' . '/lib/private/Settings/Admin/ServerDevNotice.php', 'OC\\Settings\\Admin\\Sharing' => __DIR__ . '/../../..' . '/lib/private/Settings/Admin/Sharing.php', 'OC\\Settings\\Admin\\TipsTricks' => __DIR__ . '/../../..' . '/lib/private/Settings/Admin/TipsTricks.php', 'OC\\Settings\\Application' => __DIR__ . '/../../..' . '/settings/Application.php', 'OC\\Settings\\BackgroundJobs\\VerifyUserData' => __DIR__ . '/../../..' . '/settings/BackgroundJobs/VerifyUserData.php', 'OC\\Settings\\Controller\\AdminSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/AdminSettingsController.php', 'OC\\Settings\\Controller\\AppSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/AppSettingsController.php', 'OC\\Settings\\Controller\\AuthSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/AuthSettingsController.php', 'OC\\Settings\\Controller\\CertificateController' => __DIR__ . '/../../..' . '/settings/Controller/CertificateController.php', 'OC\\Settings\\Controller\\ChangePasswordController' => __DIR__ . '/../../..' . '/settings/Controller/ChangePasswordController.php', 'OC\\Settings\\Controller\\CheckSetupController' => __DIR__ . '/../../..' . '/settings/Controller/CheckSetupController.php', 'OC\\Settings\\Controller\\EncryptionController' => __DIR__ . '/../../..' . '/settings/Controller/EncryptionController.php', 'OC\\Settings\\Controller\\GroupsController' => __DIR__ . '/../../..' . '/settings/Controller/GroupsController.php', 'OC\\Settings\\Controller\\LogSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/LogSettingsController.php', 'OC\\Settings\\Controller\\MailSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/MailSettingsController.php', 'OC\\Settings\\Controller\\SecuritySettingsController' => __DIR__ . '/../../..' . '/settings/Controller/SecuritySettingsController.php', 'OC\\Settings\\Controller\\UsersController' => __DIR__ . '/../../..' . '/settings/Controller/UsersController.php', 'OC\\Settings\\Hooks' => __DIR__ . '/../../..' . '/settings/Hooks.php', 'OC\\Settings\\Mailer\\NewUserMailHelper' => __DIR__ . '/../../..' . '/settings/Mailer/NewUserMailHelper.php', 'OC\\Settings\\Manager' => __DIR__ . '/../../..' . '/lib/private/Settings/Manager.php', 'OC\\Settings\\Mapper' => __DIR__ . '/../../..' . '/lib/private/Settings/Mapper.php', 'OC\\Settings\\Middleware\\SubadminMiddleware' => __DIR__ . '/../../..' . '/settings/Middleware/SubadminMiddleware.php', 'OC\\Settings\\RemoveOrphaned' => __DIR__ . '/../../..' . '/lib/private/Settings/RemoveOrphaned.php', 'OC\\Settings\\Section' => __DIR__ . '/../../..' . '/lib/private/Settings/Section.php', 'OC\\Setup' => __DIR__ . '/../../..' . '/lib/private/Setup.php', 'OC\\Setup\\AbstractDatabase' => __DIR__ . '/../../..' . '/lib/private/Setup/AbstractDatabase.php', 'OC\\Setup\\MySQL' => __DIR__ . '/../../..' . '/lib/private/Setup/MySQL.php', 'OC\\Setup\\OCI' => __DIR__ . '/../../..' . '/lib/private/Setup/OCI.php', 'OC\\Setup\\PostgreSQL' => __DIR__ . '/../../..' . '/lib/private/Setup/PostgreSQL.php', 'OC\\Setup\\Sqlite' => __DIR__ . '/../../..' . '/lib/private/Setup/Sqlite.php', 'OC\\Share20\\DefaultShareProvider' => __DIR__ . '/../../..' . '/lib/private/Share20/DefaultShareProvider.php', 'OC\\Share20\\Exception\\BackendError' => __DIR__ . '/../../..' . '/lib/private/Share20/Exception/BackendError.php', 'OC\\Share20\\Exception\\InvalidShare' => __DIR__ . '/../../..' . '/lib/private/Share20/Exception/InvalidShare.php', 'OC\\Share20\\Exception\\ProviderException' => __DIR__ . '/../../..' . '/lib/private/Share20/Exception/ProviderException.php', 'OC\\Share20\\Hooks' => __DIR__ . '/../../..' . '/lib/private/Share20/Hooks.php', 'OC\\Share20\\LegacyHooks' => __DIR__ . '/../../..' . '/lib/private/Share20/LegacyHooks.php', 'OC\\Share20\\Manager' => __DIR__ . '/../../..' . '/lib/private/Share20/Manager.php', 'OC\\Share20\\ProviderFactory' => __DIR__ . '/../../..' . '/lib/private/Share20/ProviderFactory.php', 'OC\\Share20\\Share' => __DIR__ . '/../../..' . '/lib/private/Share20/Share.php', 'OC\\Share20\\ShareHelper' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareHelper.php', 'OC\\Share\\Constants' => __DIR__ . '/../../..' . '/lib/private/Share/Constants.php', 'OC\\Share\\Helper' => __DIR__ . '/../../..' . '/lib/private/Share/Helper.php', 'OC\\Share\\SearchResultSorter' => __DIR__ . '/../../..' . '/lib/private/Share/SearchResultSorter.php', 'OC\\Share\\Share' => __DIR__ . '/../../..' . '/lib/private/Share/Share.php', 'OC\\Streamer' => __DIR__ . '/../../..' . '/lib/private/Streamer.php', 'OC\\SubAdmin' => __DIR__ . '/../../..' . '/lib/private/SubAdmin.php', 'OC\\SystemConfig' => __DIR__ . '/../../..' . '/lib/private/SystemConfig.php', 'OC\\SystemTag\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/SystemTag/ManagerFactory.php', 'OC\\SystemTag\\SystemTag' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTag.php', 'OC\\SystemTag\\SystemTagManager' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTagManager.php', 'OC\\SystemTag\\SystemTagObjectMapper' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTagObjectMapper.php', 'OC\\TagManager' => __DIR__ . '/../../..' . '/lib/private/TagManager.php', 'OC\\Tagging\\Tag' => __DIR__ . '/../../..' . '/lib/private/Tagging/Tag.php', 'OC\\Tagging\\TagMapper' => __DIR__ . '/../../..' . '/lib/private/Tagging/TagMapper.php', 'OC\\Tags' => __DIR__ . '/../../..' . '/lib/private/Tags.php', 'OC\\TempManager' => __DIR__ . '/../../..' . '/lib/private/TempManager.php', 'OC\\TemplateLayout' => __DIR__ . '/../../..' . '/lib/private/TemplateLayout.php', 'OC\\Template\\Base' => __DIR__ . '/../../..' . '/lib/private/Template/Base.php', 'OC\\Template\\CSSResourceLocator' => __DIR__ . '/../../..' . '/lib/private/Template/CSSResourceLocator.php', 'OC\\Template\\JSCombiner' => __DIR__ . '/../../..' . '/lib/private/Template/JSCombiner.php', 'OC\\Template\\JSConfigHelper' => __DIR__ . '/../../..' . '/lib/private/Template/JSConfigHelper.php', 'OC\\Template\\JSResourceLocator' => __DIR__ . '/../../..' . '/lib/private/Template/JSResourceLocator.php', 'OC\\Template\\ResourceLocator' => __DIR__ . '/../../..' . '/lib/private/Template/ResourceLocator.php', 'OC\\Template\\ResourceNotFoundException' => __DIR__ . '/../../..' . '/lib/private/Template/ResourceNotFoundException.php', 'OC\\Template\\SCSSCacher' => __DIR__ . '/../../..' . '/lib/private/Template/SCSSCacher.php', 'OC\\Template\\TemplateFileLocator' => __DIR__ . '/../../..' . '/lib/private/Template/TemplateFileLocator.php', 'OC\\URLGenerator' => __DIR__ . '/../../..' . '/lib/private/URLGenerator.php', 'OC\\Updater' => __DIR__ . '/../../..' . '/lib/private/Updater.php', 'OC\\Updater\\VersionCheck' => __DIR__ . '/../../..' . '/lib/private/Updater/VersionCheck.php', 'OC\\User\\Backend' => __DIR__ . '/../../..' . '/lib/private/User/Backend.php', 'OC\\User\\Database' => __DIR__ . '/../../..' . '/lib/private/User/Database.php', 'OC\\User\\LoginException' => __DIR__ . '/../../..' . '/lib/private/User/LoginException.php', 'OC\\User\\Manager' => __DIR__ . '/../../..' . '/lib/private/User/Manager.php', 'OC\\User\\NoUserException' => __DIR__ . '/../../..' . '/lib/private/User/NoUserException.php', 'OC\\User\\Session' => __DIR__ . '/../../..' . '/lib/private/User/Session.php', 'OC\\User\\User' => __DIR__ . '/../../..' . '/lib/private/User/User.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c::$prefixDirsPsr4; $loader->classMap = ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c::$classMap; }, null, ClassLoader::class); } } composer/autoload.php 0000604 00000000262 15247130453 0010714 0 ustar 00 <?php // autoload.php @generated by Composer require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInit53792487c5a8370acc0b06b1a864ff4c::getLoader(); Activity.php 0000604 00000025560 15247131625 0007063 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\ShareByMail; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\Activity\IProvider; use OCP\Contacts\IManager as IContactsManager; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory; class Activity implements IProvider { /** @var IFactory */ protected $languageFactory; /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** @var IManager */ protected $activityManager; /** @var IUserManager */ protected $userManager; /** @var IContactsManager */ protected $contactsManager; /** @var array */ protected $displayNames = []; /** @var array */ protected $contactNames = []; const SUBJECT_SHARED_EMAIL_SELF = 'shared_with_email_self'; const SUBJECT_SHARED_EMAIL_BY = 'shared_with_email_by'; const SUBJECT_SHARED_EMAIL_PASSWORD_SEND = 'shared_with_email_password_send'; const SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF = 'shared_with_email_password_send_self'; /** * @param IFactory $languageFactory * @param IURLGenerator $url * @param IManager $activityManager * @param IUserManager $userManager * @param IContactsManager $contactsManager */ public function __construct(IFactory $languageFactory, IURLGenerator $url, IManager $activityManager, IUserManager $userManager, IContactsManager $contactsManager) { $this->languageFactory = $languageFactory; $this->url = $url; $this->activityManager = $activityManager; $this->userManager = $userManager; $this->contactsManager = $contactsManager; } /** * @param string $language * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parse($language, IEvent $event, IEvent $previousEvent = null) { if ($event->getApp() !== 'sharebymail') { throw new \InvalidArgumentException(); } $this->l = $this->languageFactory->get('sharebymail', $language); if ($this->activityManager->isFormattingFilteredObject()) { try { return $this->parseShortVersion($event); } catch (\InvalidArgumentException $e) { // Ignore and simply use the long version... } } return $this->parseLongVersion($event); } /** * @param IEvent $event * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parseShortVersion(IEvent $event) { $parsedParameters = $this->getParsedParameters($event); if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_SELF) { $event->setParsedSubject($this->l->t('Shared with %1$s', [ $parsedParameters['email']['name'], ])) ->setRichSubject($this->l->t('Shared with {email}'), [ 'email' => $parsedParameters['email'], ]); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_BY) { $event->setParsedSubject($this->l->t('Shared with %1$s by %2$s', [ $parsedParameters['email']['name'], $parsedParameters['actor']['name'], ])) ->setRichSubject($this->l->t('Shared with {email} by {actor}'), [ 'email' => $parsedParameters['email'], 'actor' => $parsedParameters['actor'], ]); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND) { $event->setParsedSubject($this->l->t('Password for mail share sent to %1$s', [ $parsedParameters['email']['name'] ])) ->setRichSubject($this->l->t('Password for mail share sent to {email}'), [ 'email' => $parsedParameters['email'] ]); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF) { $event->setParsedSubject($this->l->t('Password for mail share sent to you')) ->setRichSubject($this->l->t('Password for mail share sent to you')); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } } else { throw new \InvalidArgumentException(); } return $event; } /** * @param IEvent $event * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parseLongVersion(IEvent $event) { $parsedParameters = $this->getParsedParameters($event); if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_SELF) { $event->setParsedSubject($this->l->t('You shared %1$s with %2$s by mail', [ $parsedParameters['file']['path'], $parsedParameters['email']['name'], ])) ->setRichSubject($this->l->t('You shared {file} with {email} by mail'), $parsedParameters); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_BY) { $event->setParsedSubject($this->l->t('%3$s shared %1$s with %2$s by mail', [ $parsedParameters['file']['path'], $parsedParameters['email']['name'], $parsedParameters['actor']['name'], ])) ->setRichSubject($this->l->t('{actor} shared {file} with {email} by mail'), $parsedParameters); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND) { $event->setParsedSubject($this->l->t('Password to access %1$s was sent to %2s', [ $parsedParameters['file']['path'], $parsedParameters['email']['name'] ])) ->setRichSubject($this->l->t('Password to access {file} was sent to {email}'), $parsedParameters); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF) { $event->setParsedSubject( $this->l->t('Password to access %1$s was sent to you', [$parsedParameters['file']['path']])) ->setRichSubject($this->l->t('Password to access {file} was sent to you'), $parsedParameters); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } } else { throw new \InvalidArgumentException(); } return $event; } protected function getParsedParameters(IEvent $event) { $subject = $event->getSubject(); $parameters = $event->getSubjectParameters(); switch ($subject) { case self::SUBJECT_SHARED_EMAIL_SELF: return [ 'file' => $this->generateFileParameter((int) $event->getObjectId(), $parameters[0]), 'email' => $this->generateEmailParameter($parameters[1]), ]; case self::SUBJECT_SHARED_EMAIL_BY: return [ 'file' => $this->generateFileParameter((int) $event->getObjectId(), $parameters[0]), 'email' => $this->generateEmailParameter($parameters[1]), 'actor' => $this->generateUserParameter($parameters[2]), ]; case self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND: return [ 'file' => $this->generateFileParameter((int) $event->getObjectId(), $parameters[0]), 'email' => $this->generateEmailParameter($parameters[1]), ]; case self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF: return [ 'file' => $this->generateFileParameter((int) $event->getObjectId(), $parameters[0]), ]; } throw new \InvalidArgumentException(); } /** * @param int $id * @param string $path * @return array */ protected function generateFileParameter($id, $path) { return [ 'type' => 'file', 'id' => $id, 'name' => basename($path), 'path' => trim($path, '/'), 'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]), ]; } /** * @param string $email * @return array */ protected function generateEmailParameter($email) { if (!isset($this->contactNames[$email])) { $this->contactNames[$email] = $this->getContactName($email); } return [ 'type' => 'email', 'id' => $email, 'name' => $this->contactNames[$email], ]; } /** * @param string $uid * @return array */ protected function generateUserParameter($uid) { if (!isset($this->displayNames[$uid])) { $this->displayNames[$uid] = $this->getDisplayName($uid); } return [ 'type' => 'user', 'id' => $uid, 'name' => $this->displayNames[$uid], ]; } /** * @param string $email * @return string */ protected function getContactName($email) { $addressBookContacts = $this->contactsManager->search($email, ['EMAIL']); foreach ($addressBookContacts as $contact) { if (isset($contact['isLocalSystemBook'])) { continue; } if (in_array($email, $contact['EMAIL'])) { return $contact['FN']; } } return $email; } /** * @param string $uid * @return string */ protected function getDisplayName($uid) { $user = $this->userManager->get($uid); if ($user instanceof IUser) { return $user->getDisplayName(); } else { return $uid; } } } Settings/SettingsManager.php 0000604 00000003303 15247131625 0012151 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\ShareByMail\Settings; use OCP\IConfig; class SettingsManager { /** @var IConfig */ private $config; private $sendPasswordByMailDefault = 'yes'; private $enforcePasswordProtectionDefault = 'no'; public function __construct(IConfig $config) { $this->config = $config; } /** * should the password for a mail share be send to the recipient * * @return bool */ public function sendPasswordByMail() { $sendPasswordByMail = $this->config->getAppValue('sharebymail', 'sendpasswordmail', $this->sendPasswordByMailDefault); return $sendPasswordByMail === 'yes'; } /** * do we require a share by mail to be password protected * * @return bool */ public function enforcePasswordProtection() { $enforcePassword = $this->config->getAppValue('sharebymail', 'enforcePasswordProtection', $this->enforcePasswordProtectionDefault); return $enforcePassword === 'yes'; } } ShareByMailProvider.php 0000604 00000076201 15247131625 0011140 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\ShareByMail; use OC\CapabilitiesManager; use OC\HintException; use OC\Share20\Exception\InvalidShare; use OCA\ShareByMail\Settings\SettingsManager; use OCP\Activity\IManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\IDBConnection; use OCP\IL10N; use OCP\ILogger; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\Mail\IMailer; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; use OC\Share20\Share; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IShare; use OCP\Share\IShareProvider; /** * Class ShareByMail * * @package OCA\ShareByMail */ class ShareByMailProvider implements IShareProvider { /** @var IDBConnection */ private $dbConnection; /** @var ILogger */ private $logger; /** @var ISecureRandom */ private $secureRandom; /** @var IUserManager */ private $userManager; /** @var IRootFolder */ private $rootFolder; /** @var IL10N */ private $l; /** @var IMailer */ private $mailer; /** @var IURLGenerator */ private $urlGenerator; /** @var IManager */ private $activityManager; /** @var SettingsManager */ private $settingsManager; /** @var Defaults */ private $defaults; /** @var IHasher */ private $hasher; /** @var CapabilitiesManager */ private $capabilitiesManager; /** * Return the identifier of this provider. * * @return string Containing only [a-zA-Z0-9] */ public function identifier() { return 'ocMailShare'; } /** * DefaultShareProvider constructor. * * @param IDBConnection $connection * @param ISecureRandom $secureRandom * @param IUserManager $userManager * @param IRootFolder $rootFolder * @param IL10N $l * @param ILogger $logger * @param IMailer $mailer * @param IURLGenerator $urlGenerator * @param IManager $activityManager * @param SettingsManager $settingsManager * @param Defaults $defaults * @param IHasher $hasher * @param CapabilitiesManager $capabilitiesManager */ public function __construct( IDBConnection $connection, ISecureRandom $secureRandom, IUserManager $userManager, IRootFolder $rootFolder, IL10N $l, ILogger $logger, IMailer $mailer, IURLGenerator $urlGenerator, IManager $activityManager, SettingsManager $settingsManager, Defaults $defaults, IHasher $hasher, CapabilitiesManager $capabilitiesManager ) { $this->dbConnection = $connection; $this->secureRandom = $secureRandom; $this->userManager = $userManager; $this->rootFolder = $rootFolder; $this->l = $l; $this->logger = $logger; $this->mailer = $mailer; $this->urlGenerator = $urlGenerator; $this->activityManager = $activityManager; $this->settingsManager = $settingsManager; $this->defaults = $defaults; $this->hasher = $hasher; $this->capabilitiesManager = $capabilitiesManager; } /** * Share a path * * @param IShare $share * @return IShare The share object * @throws ShareNotFound * @throws \Exception */ public function create(IShare $share) { $shareWith = $share->getSharedWith(); /* * Check if file is not already shared with the remote user */ $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); if (!empty($alreadyShared)) { $message = 'Sharing %s failed, this item is already shared with %s'; $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); throw new \Exception($message_t); } // if the admin enforces a password for all mail shares we create a // random password and send it to the recipient $password = ''; $passwordEnforced = $this->settingsManager->enforcePasswordProtection(); if ($passwordEnforced) { $password = $this->autoGeneratePassword($share); } $shareId = $this->createMailShare($share); $send = $this->sendPassword($share, $password); if ($passwordEnforced && $send === false) { $this->sendPasswordToOwner($share, $password); } $this->createShareActivity($share); $data = $this->getRawShare($shareId); return $this->createShareObject($data); } /** * auto generate password in case of password enforcement on mail shares * * @param IShare $share * @return string * @throws \Exception */ protected function autoGeneratePassword($share) { $initiatorUser = $this->userManager->get($share->getSharedBy()); $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); if ($initiatorEMailAddress === null && !$allowPasswordByMail) { throw new \Exception( $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") ); } $passwordPolicy = $this->getPasswordPolicy(); $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS; $passwordLength = 8; if (!empty($passwordPolicy)) { $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength; $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : ''; } $password = $this->secureRandom->generate($passwordLength, $passwordCharset); $share->setPassword($this->hasher->hash($password)); return $password; } /** * get password policy * * @return array */ protected function getPasswordPolicy() { $capabilities = $this->capabilitiesManager->getCapabilities(); if (isset($capabilities['password_policy'])) { return $capabilities['password_policy']; } return []; } /** * create activity if a file/folder was shared by mail * * @param IShare $share */ protected function createShareActivity(IShare $share) { $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); $this->publishActivity( Activity::SUBJECT_SHARED_EMAIL_SELF, [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], $share->getSharedBy(), $share->getNode()->getId(), $userFolder->getRelativePath($share->getNode()->getPath()) ); if ($share->getShareOwner() !== $share->getSharedBy()) { $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); $fileId = $share->getNode()->getId(); $nodes = $ownerFolder->getById($fileId); $ownerPath = $nodes[0]->getPath(); $this->publishActivity( Activity::SUBJECT_SHARED_EMAIL_BY, [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], $share->getShareOwner(), $fileId, $ownerFolder->getRelativePath($ownerPath) ); } } /** * create activity if a file/folder was shared by mail * * @param IShare $share * @param string $sharedWith * @param bool $sendToSelf */ protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) { $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); if ($sendToSelf) { $this->publishActivity( Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, [$userFolder->getRelativePath($share->getNode()->getPath())], $share->getSharedBy(), $share->getNode()->getId(), $userFolder->getRelativePath($share->getNode()->getPath()) ); } else { $this->publishActivity( Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], $share->getSharedBy(), $share->getNode()->getId(), $userFolder->getRelativePath($share->getNode()->getPath()) ); } } /** * publish activity if a file/folder was shared by mail * * @param $subject * @param $parameters * @param $affectedUser * @param $fileId * @param $filePath */ protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) { $event = $this->activityManager->generateEvent(); $event->setApp('sharebymail') ->setType('shared') ->setSubject($subject, $parameters) ->setAffectedUser($affectedUser) ->setObject('files', $fileId, $filePath); $this->activityManager->publish($event); } /** * @param IShare $share * @return int * @throws \Exception */ protected function createMailShare(IShare $share) { $share->setToken($this->generateToken()); $shareId = $this->addShareToDB( $share->getNodeId(), $share->getNodeType(), $share->getSharedWith(), $share->getSharedBy(), $share->getShareOwner(), $share->getPermissions(), $share->getToken(), $share->getPassword() ); try { $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]); $this->sendMailNotification( $share->getNode()->getName(), $link, $share->getSharedBy(), $share->getSharedWith(), $share->getExpirationDate() ); } catch (HintException $hintException) { $this->logger->error('Failed to send share by mail: ' . $hintException->getMessage()); $this->removeShareFromTable($shareId); throw $hintException; } catch (\Exception $e) { $this->logger->error('Failed to send share by mail: ' . $e->getMessage()); $this->removeShareFromTable($shareId); throw new HintException('Failed to send share by mail', $this->l->t('Failed to send share by E-mail')); } return $shareId; } /** * @param string $filename * @param string $link * @param string $initiator * @param string $shareWith * @param \DateTime|null $expiration * @throws \Exception If mail couldn't be sent */ protected function sendMailNotification($filename, $link, $initiator, $shareWith, \DateTime $expiration = null) { $initiatorUser = $this->userManager->get($initiator); $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; $subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)); $message = $this->mailer->createMessage(); $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [ 'filename' => $filename, 'link' => $link, 'initiator' => $initiatorDisplayName, 'expiration' => $expiration, 'shareWith' => $shareWith, ]); $emailTemplate->addHeader(); $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false); $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]); $emailTemplate->addBodyText( $text . ' ' . $this->l->t('Click the button below to open it.'), $text ); $emailTemplate->addBodyButton( $this->l->t('Open »%s«', [$filename]), $link ); $message->setTo([$shareWith]); // The "From" contains the sharers name $instanceName = $this->defaults->getName(); $senderName = $this->l->t( '%s via %s', [ $initiatorDisplayName, $instanceName ] ); $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); // The "Reply-To" is set to the sharer if an mail address is configured // also the default footer contains a "Do not reply" which needs to be adjusted. $initiatorEmail = $initiatorUser->getEMailAddress(); if($initiatorEmail !== null) { $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); } else { $emailTemplate->addFooter(); } $message->setSubject($subject); $message->setPlainBody($emailTemplate->renderText()); $message->setHtmlBody($emailTemplate->renderHtml()); $this->mailer->send($message); } /** * send password to recipient of a mail share * * @param IShare $share * @param string $password * @return bool */ protected function sendPassword(IShare $share, $password) { $filename = $share->getNode()->getName(); $initiator = $share->getSharedBy(); $shareWith = $share->getSharedWith(); if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) { return false; } $initiatorUser = $this->userManager->get($initiator); $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; $subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]); $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]); $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); $message = $this->mailer->createMessage(); $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [ 'filename' => $filename, 'password' => $password, 'initiator' => $initiatorDisplayName, 'initiatorEmail' => $initiatorEmailAddress, 'shareWith' => $shareWith, ]); $emailTemplate->addHeader(); $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); $emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart); $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password])); // The "From" contains the sharers name $instanceName = $this->defaults->getName(); $senderName = $this->l->t( '%s via %s', [ $initiatorDisplayName, $instanceName ] ); $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); if ($initiatorEmailAddress !== null) { $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); } else { $emailTemplate->addFooter(); } $message->setTo([$shareWith]); $message->setSubject($subject); $message->setBody($emailTemplate->renderText(), 'text/plain'); $message->setHtmlBody($emailTemplate->renderHtml()); $this->mailer->send($message); $this->createPasswordSendActivity($share, $shareWith, false); return true; } /** * send auto generated password to the owner. This happens if the admin enforces * a password for mail shares and forbid to send the password by mail to the recipient * * @param IShare $share * @param string $password * @return bool * @throws \Exception */ protected function sendPasswordToOwner(IShare $share, $password) { $filename = $share->getNode()->getName(); $initiator = $this->userManager->get($share->getSharedBy()); $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); $shareWith = $share->getSharedWith(); if ($initiatorEMailAddress === null) { throw new \Exception( $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") ); } $subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]); $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); $message = $this->mailer->createMessage(); $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [ 'filename' => $filename, 'password' => $password, 'initiator' => $initiatorDisplayName, 'initiatorEmail' => $initiatorEMailAddress, 'shareWith' => $shareWith, ]); $emailTemplate->addHeader(); $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); $emailTemplate->addBodyText($bodyPart); $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password])); $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); $emailTemplate->addFooter(); if ($initiatorEMailAddress) { $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]); } $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); $message->setSubject($subject); $message->setBody($emailTemplate->renderText(), 'text/plain'); $message->setHtmlBody($emailTemplate->renderHtml()); $this->mailer->send($message); $this->createPasswordSendActivity($share, $shareWith, true); return true; } /** * generate share token * * @return string */ protected function generateToken($size = 15) { $token = $this->secureRandom->generate( $size, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); return $token; } /** * Get all children of this share * * @param IShare $parent * @return IShare[] */ public function getChildren(IShare $parent) { $children = []; $qb = $this->dbConnection->getQueryBuilder(); $qb->select('*') ->from('share') ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) ->orderBy('id'); $cursor = $qb->execute(); while($data = $cursor->fetch()) { $children[] = $this->createShareObject($data); } $cursor->closeCursor(); return $children; } /** * add share to the database and return the ID * * @param int $itemSource * @param string $itemType * @param string $shareWith * @param string $sharedBy * @param string $uidOwner * @param int $permissions * @param string $token * @return int */ protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) { $qb = $this->dbConnection->getQueryBuilder(); $qb->insert('share') ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) ->setValue('item_type', $qb->createNamedParameter($itemType)) ->setValue('item_source', $qb->createNamedParameter($itemSource)) ->setValue('file_source', $qb->createNamedParameter($itemSource)) ->setValue('share_with', $qb->createNamedParameter($shareWith)) ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) ->setValue('permissions', $qb->createNamedParameter($permissions)) ->setValue('token', $qb->createNamedParameter($token)) ->setValue('password', $qb->createNamedParameter($password)) ->setValue('stime', $qb->createNamedParameter(time())); /* * Added to fix https://github.com/owncloud/core/issues/22215 * Can be removed once we get rid of ajax/share.php */ $qb->setValue('file_target', $qb->createNamedParameter('')); $qb->execute(); $id = $qb->getLastInsertId(); return (int)$id; } /** * Update a share * * @param IShare $share * @param string|null $plainTextPassword * @return IShare The share object */ public function update(IShare $share, $plainTextPassword = null) { $originalShare = $this->getShareById($share->getId()); // a real password was given $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; if($validPassword && $originalShare->getPassword() !== $share->getPassword()) { $this->sendPassword($share, $plainTextPassword); } /* * We allow updating the permissions and password of mail shares */ $qb = $this->dbConnection->getQueryBuilder(); $qb->update('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('password', $qb->createNamedParameter($share->getPassword())) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->execute(); return $share; } /** * @inheritdoc */ public function move(IShare $share, $recipient) { /** * nothing to do here, mail shares are only outgoing shares */ return $share; } /** * Delete a share (owner unShares the file) * * @param IShare $share */ public function delete(IShare $share) { $this->removeShareFromTable($share->getId()); } /** * @inheritdoc */ public function deleteFromSelf(IShare $share, $recipient) { // nothing to do here, mail shares are only outgoing shares return; } /** * @inheritdoc */ public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('*') ->from('share'); $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); /** * Reshares for this user are shares where they are the owner. */ if ($reshares === false) { //Special case for old shares created via the web UI $or1 = $qb->expr()->andX( $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), $qb->expr()->isNull('uid_initiator') ); $qb->andWhere( $qb->expr()->orX( $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), $or1 ) ); } else { $qb->andWhere( $qb->expr()->orX( $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) ) ); } if ($node !== null) { $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); } if ($limit !== -1) { $qb->setMaxResults($limit); } $qb->setFirstResult($offset); $qb->orderBy('id'); $cursor = $qb->execute(); $shares = []; while($data = $cursor->fetch()) { $shares[] = $this->createShareObject($data); } $cursor->closeCursor(); return $shares; } /** * @inheritdoc */ public function getShareById($id, $recipientId = null) { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('*') ->from('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); $cursor = $qb->execute(); $data = $cursor->fetch(); $cursor->closeCursor(); if ($data === false) { throw new ShareNotFound(); } try { $share = $this->createShareObject($data); } catch (InvalidShare $e) { throw new ShareNotFound(); } return $share; } /** * Get shares for a given path * * @param \OCP\Files\Node $path * @return IShare[] */ public function getSharesByPath(Node $path) { $qb = $this->dbConnection->getQueryBuilder(); $cursor = $qb->select('*') ->from('share') ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) ->execute(); $shares = []; while($data = $cursor->fetch()) { $shares[] = $this->createShareObject($data); } $cursor->closeCursor(); return $shares; } /** * @inheritdoc */ public function getSharedWith($userId, $shareType, $node, $limit, $offset) { /** @var IShare[] $shares */ $shares = []; //Get shares directly with this user $qb = $this->dbConnection->getQueryBuilder(); $qb->select('*') ->from('share'); // Order by id $qb->orderBy('id'); // Set limit and offset if ($limit !== -1) { $qb->setMaxResults($limit); } $qb->setFirstResult($offset); $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); // Filter by node if provided if ($node !== null) { $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); } $cursor = $qb->execute(); while($data = $cursor->fetch()) { $shares[] = $this->createShareObject($data); } $cursor->closeCursor(); return $shares; } /** * Get a share by token * * @param string $token * @return IShare * @throws ShareNotFound */ public function getShareByToken($token) { $qb = $this->dbConnection->getQueryBuilder(); $cursor = $qb->select('*') ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) ->execute(); $data = $cursor->fetch(); if ($data === false) { throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); } try { $share = $this->createShareObject($data); } catch (InvalidShare $e) { throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); } return $share; } /** * remove share from table * * @param string $shareId */ protected function removeShareFromTable($shareId) { $qb = $this->dbConnection->getQueryBuilder(); $qb->delete('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); $qb->execute(); } /** * Create a share object from an database row * * @param array $data * @return IShare * @throws InvalidShare * @throws ShareNotFound */ protected function createShareObject($data) { $share = new Share($this->rootFolder, $this->userManager); $share->setId((int)$data['id']) ->setShareType((int)$data['share_type']) ->setPermissions((int)$data['permissions']) ->setTarget($data['file_target']) ->setMailSend((bool)$data['mail_send']) ->setToken($data['token']); $shareTime = new \DateTime(); $shareTime->setTimestamp((int)$data['stime']); $share->setShareTime($shareTime); $share->setSharedWith($data['share_with']); $share->setPassword($data['password']); if ($data['uid_initiator'] !== null) { $share->setShareOwner($data['uid_owner']); $share->setSharedBy($data['uid_initiator']); } else { //OLD SHARE $share->setSharedBy($data['uid_owner']); $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); $owner = $path->getOwner(); $share->setShareOwner($owner->getUID()); } if ($data['expiration'] !== null) { $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); if ($expiration !== false) { $share->setExpirationDate($expiration); } } $share->setNodeId((int)$data['file_source']); $share->setNodeType($data['item_type']); $share->setProviderId($this->identifier()); return $share; } /** * Get the node with file $id for $user * * @param string $userId * @param int $id * @return \OCP\Files\File|\OCP\Files\Folder * @throws InvalidShare */ private function getNode($userId, $id) { try { $userFolder = $this->rootFolder->getUserFolder($userId); } catch (NotFoundException $e) { throw new InvalidShare(); } $nodes = $userFolder->getById($id); if (empty($nodes)) { throw new InvalidShare(); } return $nodes[0]; } /** * A user is deleted from the system * So clean up the relevant shares. * * @param string $uid * @param int $shareType */ public function userDeleted($uid, $shareType) { $qb = $this->dbConnection->getQueryBuilder(); $qb->delete('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) ->execute(); } /** * This provider does not support group shares * * @param string $gid */ public function groupDeleted($gid) { return; } /** * This provider does not support group shares * * @param string $uid * @param string $gid */ public function userDeletedFromGroup($uid, $gid) { return; } /** * get database row of a give share * * @param $id * @return array * @throws ShareNotFound */ protected function getRawShare($id) { // Now fetch the inserted share and create a complete share object $qb = $this->dbConnection->getQueryBuilder(); $qb->select('*') ->from('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); $cursor = $qb->execute(); $data = $cursor->fetch(); $cursor->closeCursor(); if ($data === false) { throw new ShareNotFound; } return $data; } public function getSharesInFolder($userId, Folder $node, $reshares) { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('*') ->from('share', 's') ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) ->andWhere( $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) ); /** * Reshares for this user are shares where they are the owner. */ if ($reshares === false) { $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); } else { $qb->andWhere( $qb->expr()->orX( $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) ) ); } $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); $qb->orderBy('id'); $cursor = $qb->execute(); $shares = []; while ($data = $cursor->fetch()) { $shares[$data['fileid']][] = $this->createShareObject($data); } $cursor->closeCursor(); return $shares; } /** * @inheritdoc */ public function getAccessList($nodes, $currentAccess) { $ids = []; foreach ($nodes as $node) { $ids[] = $node->getId(); } $qb = $this->dbConnection->getQueryBuilder(); $qb->select('share_with') ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) ->andWhere($qb->expr()->orX( $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) )) ->setMaxResults(1); $cursor = $qb->execute(); $mail = $cursor->fetch() !== false; $cursor->closeCursor(); return ['public' => $mail]; } } Settings.php 0000604 00000003263 15247131625 0007063 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\ShareByMail; use OCA\ShareByMail\Settings\SettingsManager; class Settings { /** @var SettingsManager */ private $settingsManager; public function __construct(SettingsManager $settingsManager) { $this->settingsManager = $settingsManager; } /** * announce that the share-by-mail share provider is enabled * * @param array $settings */ public function announceShareProvider(array $settings) { $array = json_decode($settings['array']['oc_appconfig'], true); $array['shareByMailEnabled'] = true; $settings['array']['oc_appconfig'] = json_encode($array); } public function announceShareByMailSettings(array $settings) { $array = json_decode($settings['array']['oc_appconfig'], true); $array['shareByMail']['enforcePasswordProtection'] = $this->settingsManager->enforcePasswordProtection(); $settings['array']['oc_appconfig'] = json_encode($array); } } Controller/LogController.php 0000604 00000010131 15247160551 0012163 0 ustar 00 <?php /** * @author Robin Appelman <icewind@owncloud.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\LogReader\Controller; use OCA\LogReader\Log\LogIterator; use OCA\LogReader\Log\SearchFilter; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\IRequest; /** * Class LogController * * @package OCA\LogReader\Controller */ class LogController extends Controller { /** * @var IConfig */ private $config; public function __construct($appName, IRequest $request, IConfig $config) { parent::__construct($appName, $request); $this->config = $config; } private function getLogIterator() { $dateFormat = $this->config->getSystemValue('logdateformat', \DateTime::ATOM); $timezone = $this->config->getSystemValue('logtimezone', 'UTC'); $logClasses = ['\OC\Log\Owncloud', '\OC_Log_Owncloud', '\OC\Log\File']; foreach ($logClasses as $logClass) { if (class_exists($logClass)) { $handle = fopen($logClass::getLogFilePath(), 'rb'); if ($handle) { return new LogIterator($handle, $dateFormat, $timezone); } else { throw new \Exception("Error while opening ".$logClass::getLogFilePath()); } } } throw new \Exception('Can\'t find log class'); } /** * @param int $count * @param int $offset * @return TemplateResponse */ public function get($count = 50, $offset = 0) { $iterator = $this->getLogIterator(); return $this->responseFromIterator($iterator, $count, $offset); } /** * @param string $query * @param int $count * @param int $offset * @return TemplateResponse * * @NoCSRFRequired */ public function search($query = '', $count = 50, $offset = 0) { $iterator = $this->getLogIterator(); $iterator = new \LimitIterator($iterator, 0, 100000); // limit the number of message we search to avoid huge search times $iterator->rewind(); $iterator = new SearchFilter($iterator, $query); $iterator->rewind(); return $this->responseFromIterator($iterator, $count, $offset); } public function getLevels() { return new JSONResponse($this->config->getAppValue('logreader', 'levels', '11111')); } public function getSettings() { return new JSONResponse([ 'levels' => $this->config->getAppValue('logreader', 'levels', '11111'), 'dateformat' => $this->config->getSystemValue('logdateformat', \DateTime::ISO8601), 'timezone' => $this->config->getSystemValue('logtimezone', 'UTC'), 'relativedates' => (bool)$this->config->getAppValue('logreader', 'relativedates', false), ]); } /** * @param bool $relative */ public function setRelative($relative) { $this->config->setAppValue('logreader', 'relativedates', $relative); } public function setLevels($levels) { $intLevels = array_map('intval', str_split($levels)); $minLevel = 4; foreach ($intLevels as $level => $log) { if ($log) { $minLevel = $level; break; } } $this->config->setSystemValue('loglevel', $minLevel); $this->config->setAppValue('logreader', 'levels', $levels); return $minLevel; } protected function responseFromIterator(\Iterator $iterator, $count, $offset) { for ($i = 0; $i < $offset; $i++) { $iterator->next(); } $data = []; for ($i = 0; $i < $count && $iterator->valid(); $i++) { $line = $iterator->current(); if (!is_null($line)) { $data[] = $line; } $iterator->next(); } return new JSONResponse([ 'data' => $data, 'remain' => $iterator->valid() ]); } } Log/SearchFilter.php 0000604 00000003365 15247160551 0010362 0 ustar 00 <?php /** * @author Robin Appelman <icewind@owncloud.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\LogReader\Log; class SearchFilter extends \FilterIterator { /** * @var string */ private $query; /** * @var string[] */ private $levels; /** * @param \Iterator $iterator * @param string $query */ public function __construct(\Iterator $iterator, $query) { parent::__construct($iterator); $this->rewind(); $this->query = strtolower($query); $this->levels = ['Debug', 'Info', 'Warning', 'Error', 'Fatal']; } private function formatLevel($level) { return isset($this->levels[$level]) ? $this->levels[$level] : 'Unknown'; } public function accept() { if (!$this->query) { return true; } $value = $this->current(); return stripos($value['message'], $this->query) !== false || stripos($value['app'], $this->query) !== false || stripos($value['reqId'], $this->query) !== false || stripos($value['user'], $this->query) !== false || stripos($value['url'], $this->query) !== false || stripos($this->formatLevel($value['level']), $this->query) !== false; } } Log/LogIterator.php 0000604 00000005301 15247160551 0010232 0 ustar 00 <?php /** * @author Robin Appelman <icewind@owncloud.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\LogReader\Log; class LogIterator implements \Iterator { /** * @var resource */ private $handle; /** * @var int */ private $position = 0; /** * @var string */ private $lastLine; /** * @var string */ private $currentLine = ''; private $currentKey = -1; /** * @var string */ private $dateFormat; private $timezone; const CHUNK_SIZE = 100; // how many chars do we try at once to find a new line /** * @param resource $handle * @param string $dateFormat * @param string $timezone */ public function __construct($handle, $dateFormat, $timezone) { $this->handle = $handle; $this->dateFormat = $dateFormat; $this->timezone = new \DateTimeZone($timezone); $this->rewind(); $this->next(); } function rewind() { fseek($this->handle, 0, SEEK_END); $this->position = ftell($this->handle) - self::CHUNK_SIZE; $this->currentKey = 0; } function current() { $entry = json_decode($this->lastLine, true); if ($this->dateFormat !== \DateTime::ATOM) { $time = \DateTime::createFromFormat($this->dateFormat, $entry['time'], $this->timezone); if ($time) { $entry['time'] = $time->format(\DateTime::ATOM); } } return $entry; } function key() { return $this->currentKey; } function next() { // Loop through each character of the file looking for new lines while ($this->position >= 0) { fseek($this->handle, $this->position); $chars = fread($this->handle, self::CHUNK_SIZE); $newlinePos = strrpos($chars, "\n"); if ($newlinePos !== false) { $this->currentLine = substr($chars, $newlinePos + 1) . $this->currentLine; $this->lastLine = $this->currentLine; $this->currentKey++; $this->currentLine = ''; $this->position -= (self::CHUNK_SIZE - $newlinePos); return; } else { $this->currentLine = $chars . $this->currentLine; $this->position -= self::CHUNK_SIZE; } } } function valid() { return $this->position >= 0 && is_resource($this->handle); } } Controller/Notifications.php 0000604 00000006711 15247164144 0012222 0 ustar 00 <?php /** * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Comments\Controller; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\Response; use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; use OCP\Files\Folder; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUserSession; use OCP\Notification\IManager; /** * Class Notifications * * @package OCA\Comments\Controller */ class Notifications extends Controller { /** @var Folder */ protected $folder; /** @var ICommentsManager */ protected $commentsManager; /** @var IURLGenerator */ protected $urlGenerator; /** @var IManager */ protected $notificationManager; /** @var IUserSession */ protected $userSession; /** * Notifications constructor. * * @param string $appName * @param IRequest $request * @param ICommentsManager $commentsManager * @param Folder $folder * @param IURLGenerator $urlGenerator * @param IManager $notificationManager * @param IUserSession $userSession */ public function __construct( $appName, IRequest $request, ICommentsManager $commentsManager, Folder $folder, IURLGenerator $urlGenerator, IManager $notificationManager, IUserSession $userSession ) { parent::__construct($appName, $request); $this->commentsManager = $commentsManager; $this->folder = $folder; $this->urlGenerator = $urlGenerator; $this->notificationManager = $notificationManager; $this->userSession = $userSession; } /** * @NoAdminRequired * @NoCSRFRequired * * @param string $id the comment ID * @return Response */ public function view($id) { try { $comment = $this->commentsManager->get($id); if($comment->getObjectType() !== 'files') { return new NotFoundResponse(); } $files = $this->folder->getById($comment->getObjectId()); if(count($files) === 0) { $this->markProcessed($comment); return new NotFoundResponse(); } $url = $this->urlGenerator->linkToRouteAbsolute( 'files.viewcontroller.showFile', [ 'fileid' => $comment->getObjectId() ] ); $this->markProcessed($comment); return new RedirectResponse($url); } catch (\Exception $e) { return new NotFoundResponse(); } } /** * Marks the notification about a comment as processed * @param IComment $comment */ protected function markProcessed(IComment $comment) { $user = $this->userSession->getUser(); if(is_null($user)) { return; } $notification = $this->notificationManager->createNotification(); $notification->setApp('comments') ->setObject('comment', $comment->getId()) ->setSubject('mention') ->setUser($user->getUID()); $this->notificationManager->markProcessed($notification); } } Activity/Setting.php 0000604 00000004360 15247164144 0010475 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Comments\Activity; use OCP\Activity\ISetting; use OCP\IL10N; class Setting implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'comments'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('<strong>Comments</strong> for files'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 50; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } Activity/Listener.php 0000604 00000007540 15247164144 0010650 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Comments\Activity; use OCP\Activity\IManager; use OCP\App\IAppManager; use OCP\Comments\CommentsEvent; use OCP\Files\Config\IMountProviderCollection; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\IUser; use OCP\IUserSession; use OCP\Share; use OCP\Share\IShareHelper; class Listener { /** @var IManager */ protected $activityManager; /** @var IUserSession */ protected $session; /** @var \OCP\App\IAppManager */ protected $appManager; /** @var \OCP\Files\Config\IMountProviderCollection */ protected $mountCollection; /** @var \OCP\Files\IRootFolder */ protected $rootFolder; /** @var IShareHelper */ protected $shareHelper; /** * Listener constructor. * * @param IManager $activityManager * @param IUserSession $session * @param IAppManager $appManager * @param IMountProviderCollection $mountCollection * @param IRootFolder $rootFolder * @param IShareHelper $shareHelper */ public function __construct(IManager $activityManager, IUserSession $session, IAppManager $appManager, IMountProviderCollection $mountCollection, IRootFolder $rootFolder, IShareHelper $shareHelper) { $this->activityManager = $activityManager; $this->session = $session; $this->appManager = $appManager; $this->mountCollection = $mountCollection; $this->rootFolder = $rootFolder; $this->shareHelper = $shareHelper; } /** * @param CommentsEvent $event */ public function commentEvent(CommentsEvent $event) { if ($event->getComment()->getObjectType() !== 'files' || !in_array($event->getEvent(), [CommentsEvent::EVENT_ADD]) || !$this->appManager->isInstalled('activity')) { // Comment not for file, not adding a comment or no activity-app enabled (save the energy) return; } // Get all mount point owners $cache = $this->mountCollection->getMountCache(); $mounts = $cache->getMountsForFileId($event->getComment()->getObjectId()); if (empty($mounts)) { return; } $users = []; foreach ($mounts as $mount) { $owner = $mount->getUser()->getUID(); $ownerFolder = $this->rootFolder->getUserFolder($owner); $nodes = $ownerFolder->getById($event->getComment()->getObjectId()); if (!empty($nodes)) { /** @var Node $node */ $node = array_shift($nodes); $al = $this->shareHelper->getPathsForAccessList($node); $users = array_merge($users, $al['users']); } } $actor = $this->session->getUser(); if ($actor instanceof IUser) { $actor = $actor->getUID(); } else { $actor = ''; } $activity = $this->activityManager->generateEvent(); $activity->setApp('comments') ->setType('comments') ->setAuthor($actor) ->setObject($event->getComment()->getObjectType(), (int) $event->getComment()->getObjectId()) ->setMessage('add_comment_message', [ 'commentId' => $event->getComment()->getId(), ]); foreach ($users as $user => $path) { $activity->setAffectedUser($user); $activity->setSubject('add_comment_subject', [ 'actor' => $actor, 'fileId' => (int) $event->getComment()->getObjectId(), 'filePath' => trim($path, '/'), ]); $this->activityManager->publish($activity); } } } Activity/Filter.php 0000604 00000004072 15247164144 0010305 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Comments\Activity; use OCP\Activity\IFilter; use OCP\IL10N; use OCP\IURLGenerator; class Filter implements IFilter { /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * @return string Lowercase a-z only identifier * @since 11.0.0 */ public function getIdentifier() { return 'comments'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('Comments'); } /** * @return int * @since 11.0.0 */ public function getPriority() { return 40; } /** * @return string Full URL to an icon, empty string when none is given * @since 11.0.0 */ public function getIcon() { return $this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg')); } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function filterTypes(array $types) { return $types; } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function allowedApps() { return ['comments']; } } EventHandler.php 0000604 00000004726 15247164145 0007652 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\Comments; use OCA\Comments\Activity\Listener as ActivityListener; use OCA\Comments\AppInfo\Application; use OCA\Comments\Notification\Listener as NotificationListener; use OCP\Comments\CommentsEvent; use OCP\Comments\ICommentsEventHandler; /** * Class EventHandler * * @package OCA\Comments */ class EventHandler implements ICommentsEventHandler { /** @var ActivityListener */ private $activityListener; /** @var NotificationListener */ private $notificationListener; public function __construct(ActivityListener $activityListener, NotificationListener $notificationListener) { $this->activityListener = $activityListener; $this->notificationListener = $notificationListener; } /** * @param CommentsEvent $event */ public function handle(CommentsEvent $event) { if($event->getComment()->getObjectType() !== 'files') { // this is a 'files'-specific Handler return; } $eventType = $event->getEvent(); if( $eventType === CommentsEvent::EVENT_ADD ) { $this->notificationHandler($event); $this->activityHandler($event); return; } $applicableEvents = [ CommentsEvent::EVENT_PRE_UPDATE, CommentsEvent::EVENT_UPDATE, CommentsEvent::EVENT_DELETE, ]; if(in_array($eventType, $applicableEvents)) { $this->notificationHandler($event); return; } } /** * @param CommentsEvent $event */ private function activityHandler(CommentsEvent $event) { $this->activityListener->commentEvent($event); } /** * @param CommentsEvent $event */ private function notificationHandler(CommentsEvent $event) { $this->notificationListener->evaluate($event); } } Notification/Notifier.php 0000604 00000011550 15247164145 0011471 0 ustar 00 <?php /** * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Comments\Notification; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; use OCP\Files\IRootFolder; use OCP\IURLGenerator; use OCP\IUserManager; use OCP\L10N\IFactory; use OCP\Notification\INotification; use OCP\Notification\INotifier; class Notifier implements INotifier { /** @var IFactory */ protected $l10nFactory; /** @var IRootFolder */ protected $rootFolder; /** @var ICommentsManager */ protected $commentsManager; /** @var IURLGenerator */ protected $url; /** @var IUserManager */ protected $userManager; public function __construct( IFactory $l10nFactory, IRootFolder $rootFolder, ICommentsManager $commentsManager, IURLGenerator $url, IUserManager $userManager ) { $this->l10nFactory = $l10nFactory; $this->rootFolder = $rootFolder; $this->commentsManager = $commentsManager; $this->url = $url; $this->userManager = $userManager; } /** * @param INotification $notification * @param string $languageCode The code of the language that should be used to prepare the notification * @return INotification * @throws \InvalidArgumentException When the notification was not prepared by a notifier */ public function prepare(INotification $notification, $languageCode) { if($notification->getApp() !== 'comments') { throw new \InvalidArgumentException(); } try { $comment = $this->commentsManager->get($notification->getObjectId()); } catch(NotFoundException $e) { // needs to be converted to InvalidArgumentException, otherwise none Notifications will be shown at all throw new \InvalidArgumentException('Comment not found', 0, $e); } $l = $this->l10nFactory->get('comments', $languageCode); $displayName = $comment->getActorId(); $isDeletedActor = $comment->getActorType() === ICommentsManager::DELETED_USER; if($comment->getActorType() === 'users') { $commenter = $this->userManager->get($comment->getActorId()); if(!is_null($commenter)) { $displayName = $commenter->getDisplayName(); } } switch($notification->getSubject()) { case 'mention': $parameters = $notification->getSubjectParameters(); if($parameters[0] !== 'files') { throw new \InvalidArgumentException('Unsupported comment object'); } $userFolder = $this->rootFolder->getUserFolder($notification->getUser()); $nodes = $userFolder->getById($parameters[1]); if(empty($nodes)) { throw new \InvalidArgumentException('Cannot resolve file id to Node instance'); } $node = $nodes[0]; if ($isDeletedActor) { $notification->setParsedSubject($l->t( 'A (now) deleted user mentioned you in a comment on “%s”', [$node->getName()] )) ->setRichSubject( $l->t('A (now) deleted user mentioned you in a comment on “{file}”'), [ 'file' => [ 'type' => 'file', 'id' => $comment->getObjectId(), 'name' => $node->getName(), 'path' => $node->getPath(), 'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $comment->getObjectId()]), ], ] ); } else { $notification->setParsedSubject($l->t( '%1$s mentioned you in a comment on “%2$s”', [$displayName, $node->getName()] )) ->setRichSubject( $l->t('{user} mentioned you in a comment on “{file}”'), [ 'user' => [ 'type' => 'user', 'id' => $comment->getActorId(), 'name' => $displayName, ], 'file' => [ 'type' => 'file', 'id' => $comment->getObjectId(), 'name' => $node->getName(), 'path' => $node->getPath(), 'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $comment->getObjectId()]), ], ] ); } $notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg'))) ->setLink($this->url->linkToRouteAbsolute( 'comments.Notifications.view', ['id' => $comment->getId()]) ); return $notification; break; default: throw new \InvalidArgumentException('Invalid subject'); } } } Notification/Listener.php 0000604 00000006121 15247164145 0011475 0 ustar 00 <?php /** * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\Comments\Notification; use OCP\Comments\CommentsEvent; use OCP\Comments\IComment; use OCP\IUserManager; use OCP\Notification\IManager; class Listener { /** @var IManager */ protected $notificationManager; /** @var IUserManager */ protected $userManager; /** * Listener constructor. * * @param IManager $notificationManager * @param IUserManager $userManager */ public function __construct( IManager $notificationManager, IUserManager $userManager ) { $this->notificationManager = $notificationManager; $this->userManager = $userManager; } /** * @param CommentsEvent $event */ public function evaluate(CommentsEvent $event) { $comment = $event->getComment(); $mentions = $this->extractMentions($comment->getMentions()); if(empty($mentions)) { // no one to notify return; } $notification = $this->instantiateNotification($comment); foreach($mentions as $uid) { if( ($comment->getActorType() === 'users' && $uid === $comment->getActorId()) || !$this->userManager->userExists($uid) ) { // do not notify unknown users or yourself continue; } $notification->setUser($uid); if( $event->getEvent() === CommentsEvent::EVENT_DELETE || $event->getEvent() === CommentsEvent::EVENT_PRE_UPDATE) { $this->notificationManager->markProcessed($notification); } else { $this->notificationManager->notify($notification); } } } /** * creates a notification instance and fills it with comment data * * @param IComment $comment * @return \OCP\Notification\INotification */ public function instantiateNotification(IComment $comment) { $notification = $this->notificationManager->createNotification(); $notification ->setApp('comments') ->setObject('comment', $comment->getId()) ->setSubject('mention', [ $comment->getObjectType(), $comment->getObjectId() ]) ->setDateTime($comment->getCreationDateTime()); return $notification; } /** * flattens the mention array returned from comments to a list of user ids. * * @param array $mentions * @return string[] containing the mentions, e.g. ['alice', 'bob'] */ public function extractMentions(array $mentions) { if(empty($mentions)) { return []; } $uids = []; foreach($mentions as $mention) { if($mention['type'] === 'user') { $uids[] = $mention['id']; } } return $uids; } } Upload/UploadHome.php 0000604 00000004623 15247164651 0010552 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Upload; use OC\Files\Filesystem; use OC\Files\View; use OCA\DAV\Connector\Sabre\Directory; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\ICollection; class UploadHome implements ICollection { /** * UploadHome constructor. * * @param array $principalInfo */ public function __construct($principalInfo) { $this->principalInfo = $principalInfo; } function createFile($name, $data = null) { throw new Forbidden('Permission denied to create file (filename ' . $name . ')'); } function createDirectory($name) { $this->impl()->createDirectory($name); } function getChild($name) { return new UploadFolder($this->impl()->getChild($name)); } function getChildren() { return array_map(function($node) { return new UploadFolder($node); }, $this->impl()->getChildren()); } function childExists($name) { return !is_null($this->getChild($name)); } function delete() { $this->impl()->delete(); } function getName() { return 'uploads'; } function setName($name) { throw new Forbidden('Permission denied to rename this folder'); } function getLastModified() { return $this->impl()->getLastModified(); } /** * @return Directory */ private function impl() { $rootView = new View(); $user = \OC::$server->getUserSession()->getUser(); Filesystem::initMountPoints($user->getUID()); if (!$rootView->file_exists('/' . $user->getUID() . '/uploads')) { $rootView->mkdir('/' . $user->getUID() . '/uploads'); } $view = new View('/' . $user->getUID() . '/uploads'); $rootInfo = $view->getFileInfo(''); $impl = new Directory($view, $rootInfo); return $impl; } } Upload/UploadFolder.php 0000604 00000003755 15247164651 0011102 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Upload; use OCA\DAV\Connector\Sabre\Directory; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\ICollection; class UploadFolder implements ICollection { private $node; function __construct(Directory $node) { $this->node = $node; } function createFile($name, $data = null) { // TODO: verify name - should be a simple number $this->node->createFile($name, $data); } function createDirectory($name) { throw new Forbidden('Permission denied to create file (filename ' . $name . ')'); } function getChild($name) { if ($name === '.file') { return new FutureFile($this->node, '.file'); } return $this->node->getChild($name); } function getChildren() { $children = $this->node->getChildren(); $children[] = new FutureFile($this->node, '.file'); return $children; } function childExists($name) { if ($name === '.file') { return true; } return $this->node->childExists($name); } function delete() { $this->node->delete(); } function getName() { return $this->node->getName(); } function setName($name) { throw new Forbidden('Permission denied to rename this folder'); } function getLastModified() { return $this->node->getLastModified(); } } Upload/FutureFile.php 0000604 00000004617 15247164651 0010572 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Upload; use OCA\DAV\Connector\Sabre\Directory; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\IFile; /** * Class FutureFile * * The FutureFile is a SabreDav IFile which connects the chunked upload directory * with the AssemblyStream, who does the final assembly job * * @package OCA\DAV\Upload */ class FutureFile implements \Sabre\DAV\IFile { /** @var Directory */ private $root; /** @var string */ private $name; /** * @param Directory $root * @param string $name */ function __construct(Directory $root, $name) { $this->root = $root; $this->name = $name; } /** * @inheritdoc */ function put($data) { throw new Forbidden('Permission denied to put into this file'); } /** * @inheritdoc */ function get() { $nodes = $this->root->getChildren(); return AssemblyStream::wrap($nodes); } /** * @inheritdoc */ function getContentType() { return 'application/octet-stream'; } /** * @inheritdoc */ function getETag() { return $this->root->getETag(); } /** * @inheritdoc */ function getSize() { $children = $this->root->getChildren(); $sizes = array_map(function($node) { /** @var IFile $node */ return $node->getSize(); }, $children); return array_sum($sizes); } /** * @inheritdoc */ function delete() { $this->root->delete(); } /** * @inheritdoc */ function getName() { return $this->name; } /** * @inheritdoc */ function setName($name) { throw new Forbidden('Permission denied to rename this file'); } /** * @inheritdoc */ function getLastModified() { return $this->root->getLastModified(); } } Upload/AssemblyStream.php 0000604 00000013622 15247164651 0011447 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Upload; use Sabre\DAV\IFile; /** * Class AssemblyStream * * The assembly stream is a virtual stream that wraps multiple chunks. * Reading from the stream transparently accessed the underlying chunks and * give a representation as if they were already merged together. * * @package OCA\DAV\Upload */ class AssemblyStream implements \Icewind\Streams\File { /** @var resource */ private $context; /** @var IFile[] */ private $nodes; /** @var int */ private $pos = 0; /** @var array */ private $sortedNodes; /** @var int */ private $size; /** @var resource */ private $currentStream = null; /** * @param string $path * @param string $mode * @param int $options * @param string &$opened_path * @return bool */ public function stream_open($path, $mode, $options, &$opened_path) { $this->loadContext('assembly'); // sort the nodes $nodes = $this->nodes; // http://stackoverflow.com/a/10985500 @usort($nodes, function(IFile $a, IFile $b) { return strnatcmp($a->getName(), $b->getName()); }); $this->nodes = $nodes; // build additional information $this->sortedNodes = []; $start = 0; foreach($this->nodes as $node) { $size = $node->getSize(); $name = $node->getName(); $this->sortedNodes[$name] = ['node' => $node, 'start' => $start, 'end' => $start + $size]; $start += $size; $this->size = $start; } return true; } /** * @param string $offset * @param int $whence * @return bool */ public function stream_seek($offset, $whence = SEEK_SET) { return false; } /** * @return int */ public function stream_tell() { return $this->pos; } /** * @param int $count * @return string */ public function stream_read($count) { do { if ($this->currentStream === null) { list($node, $posInNode) = $this->getNodeForPosition($this->pos); if (is_null($node)) { // reached last node, no more data return ''; } $this->currentStream = $this->getStream($node); fseek($this->currentStream, $posInNode); } $data = fread($this->currentStream, $count); // isset is faster than strlen if (isset($data[$count - 1])) { // we read the full count $read = $count; } else { // reaching end of stream, which happens less often so strlen is ok $read = strlen($data); } if (feof($this->currentStream)) { fclose($this->currentStream); $this->currentNode = null; $this->currentStream = null; } // if no data read, try again with the next node because // returning empty data can make the caller think there is no more // data left to read } while ($read === 0); // update position $this->pos += $read; return $data; } /** * @param string $data * @return int */ public function stream_write($data) { return false; } /** * @param int $option * @param int $arg1 * @param int $arg2 * @return bool */ public function stream_set_option($option, $arg1, $arg2) { return false; } /** * @param int $size * @return bool */ public function stream_truncate($size) { return false; } /** * @return array */ public function stream_stat() { return []; } /** * @param int $operation * @return bool */ public function stream_lock($operation) { return false; } /** * @return bool */ public function stream_flush() { return false; } /** * @return bool */ public function stream_eof() { return $this->pos >= $this->size; } /** * @return bool */ public function stream_close() { return true; } /** * Load the source from the stream context and return the context options * * @param string $name * @return array * @throws \Exception */ protected function loadContext($name) { $context = stream_context_get_options($this->context); if (isset($context[$name])) { $context = $context[$name]; } else { throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set'); } if (isset($context['nodes']) and is_array($context['nodes'])) { $this->nodes = $context['nodes']; } else { throw new \BadMethodCallException('Invalid context, nodes not set'); } return $context; } /** * @param IFile[] $nodes * @return resource * * @throws \BadMethodCallException */ public static function wrap(array $nodes) { $context = stream_context_create([ 'assembly' => [ 'nodes' => $nodes] ]); stream_wrapper_register('assembly', '\OCA\DAV\Upload\AssemblyStream'); try { $wrapped = fopen('assembly://', 'r', null, $context); } catch (\BadMethodCallException $e) { stream_wrapper_unregister('assembly'); throw $e; } stream_wrapper_unregister('assembly'); return $wrapped; } /** * @param $pos * @return IFile | null */ private function getNodeForPosition($pos) { foreach($this->sortedNodes as $node) { if ($pos >= $node['start'] && $pos < $node['end']) { return [$node['node'], $pos - $node['start']]; } } return null; } /** * @param IFile $node * @return resource */ private function getStream(IFile $node) { $data = $node->get(); if (is_resource($data)) { return $data; } return fopen('data://text/plain,' . $data,'r'); } } Upload/RootCollection.php 0000604 00000002163 15247164651 0011451 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Upload; use Sabre\DAVACL\AbstractPrincipalCollection; class RootCollection extends AbstractPrincipalCollection { /** * @inheritdoc */ function getChildForPrincipal(array $principalInfo) { return new UploadHome($principalInfo); } /** * @inheritdoc */ function getName() { return 'uploads'; } } HookManager.php 0000604 00000007671 15247164651 0007472 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\CardDAV\SyncService; use OCP\IUser; use OCP\IUserManager; use OCP\Util; use Symfony\Component\EventDispatcher\EventDispatcher; class HookManager { /** @var IUserManager */ private $userManager; /** @var SyncService */ private $syncService; /** @var IUser[] */ private $usersToDelete; /** @var CalDavBackend */ private $calDav; /** @var CardDavBackend */ private $cardDav; /** @var array */ private $calendarsToDelete; /** @var array */ private $addressBooksToDelete; /** @var EventDispatcher */ private $eventDispatcher; public function __construct(IUserManager $userManager, SyncService $syncService, CalDavBackend $calDav, CardDavBackend $cardDav, EventDispatcher $eventDispatcher) { $this->userManager = $userManager; $this->syncService = $syncService; $this->calDav = $calDav; $this->cardDav = $cardDav; $this->eventDispatcher = $eventDispatcher; } public function setup() { Util::connectHook('OC_User', 'post_createUser', $this, 'postCreateUser'); Util::connectHook('OC_User', 'pre_deleteUser', $this, 'preDeleteUser'); Util::connectHook('OC_User', 'post_deleteUser', $this, 'postDeleteUser'); Util::connectHook('OC_User', 'changeUser', $this, 'changeUser'); } public function postCreateUser($params) { $user = $this->userManager->get($params['uid']); $this->syncService->updateUser($user); } public function preDeleteUser($params) { $uid = $params['uid']; $this->usersToDelete[$uid] = $this->userManager->get($uid); $this->calendarsToDelete = $this->calDav->getUsersOwnCalendars('principals/users/' . $uid); $this->addressBooksToDelete = $this->cardDav->getUsersOwnAddressBooks('principals/users/' . $uid); } public function postDeleteUser($params) { $uid = $params['uid']; if (isset($this->usersToDelete[$uid])){ $this->syncService->deleteUser($this->usersToDelete[$uid]); } foreach ($this->calendarsToDelete as $calendar) { $this->calDav->deleteCalendar($calendar['id']); } $this->calDav->deleteAllSharesByUser('principals/users/' . $uid); foreach ($this->addressBooksToDelete as $addressBook) { $this->cardDav->deleteAddressBook($addressBook['id']); } } public function changeUser($params) { $user = $params['user']; $this->syncService->updateUser($user); } public function firstLogin(IUser $user = null) { if (!is_null($user)) { $principal = 'principals/users/' . $user->getUID(); if ($this->calDav->getCalendarsForUserCount($principal) === 0) { try { $this->calDav->createCalendar($principal, CalDavBackend::PERSONAL_CALENDAR_URI, [ '{DAV:}displayname' => CalDavBackend::PERSONAL_CALENDAR_NAME, ]); } catch (\Exception $ex) { \OC::$server->getLogger()->logException($ex); } } if ($this->cardDav->getAddressBooksForUserCount($principal) === 0) { try { $this->cardDav->createAddressBook($principal, CardDavBackend::PERSONAL_ADDRESSBOOK_URI, [ '{DAV:}displayname' => CardDavBackend::PERSONAL_ADDRESSBOOK_NAME, ]); } catch (\Exception $ex) { \OC::$server->getLogger()->logException($ex); } } } } } Avatars/AvatarNode.php 0000604 00000004055 15247164651 0010715 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2016, ownCloud GmbH * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Avatars; use OCP\IAvatar; use Sabre\DAV\File; class AvatarNode extends File { private $ext; private $size; private $avatar; /** * AvatarNode constructor. * * @param integer $size * @param string $ext * @param IAvatar $avatar */ public function __construct($size, $ext, $avatar) { $this->size = $size; $this->ext = $ext; $this->avatar = $avatar; } /** * Returns the name of the node. * * This is used to generate the url. * * @return string */ public function getName() { return "$this->size.$this->ext"; } public function get() { $image = $this->avatar->get($this->size); $res = $image->resource(); ob_start(); if ($this->ext === 'png') { imagepng($res); } else { imagejpeg($res); } return ob_get_clean(); } /** * Returns the mime-type for a file * * If null is returned, we'll assume application/octet-stream * * @return string|null */ public function getContentType() { if ($this->ext === 'png') { return 'image/png'; } return 'image/jpeg'; } public function getETag() { return $this->avatar->getFile($this->size)->getEtag(); } public function getLastModified() { $timestamp = $this->avatar->getFile($this->size)->getMTime(); if (!empty($timestamp)) { return (int)$timestamp; } return $timestamp; } } Avatars/AvatarHome.php 0000604 00000005730 15247164651 0010721 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2016, ownCloud GmbH * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Avatars; use OCP\IAvatarManager; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\ICollection; use Sabre\Uri; class AvatarHome implements ICollection { /** @var array */ private $principalInfo; /** @var IAvatarManager */ private $avatarManager; /** * AvatarHome constructor. * * @param array $principalInfo * @param IAvatarManager $avatarManager */ public function __construct($principalInfo, IAvatarManager $avatarManager) { $this->principalInfo = $principalInfo; $this->avatarManager = $avatarManager; } public function createFile($name, $data = null) { throw new Forbidden('Permission denied to create a file'); } public function createDirectory($name) { throw new Forbidden('Permission denied to create a folder'); } public function getChild($name) { $elements = pathinfo($name); $ext = isset($elements['extension']) ? $elements['extension'] : ''; $size = (int)(isset($elements['filename']) ? $elements['filename'] : '64'); if (!in_array($ext, ['jpeg', 'png'], true)) { throw new MethodNotAllowed('File format not allowed'); } if ($size <= 0 || $size > 1024) { throw new MethodNotAllowed('Invalid image size'); } $avatar = $this->avatarManager->getAvatar($this->getName()); if ($avatar === null || !$avatar->exists()) { throw new NotFound(); } return new AvatarNode($size, $ext, $avatar); } public function getChildren() { try { return [ $this->getChild('96.jpeg') ]; } catch(NotFound $exception) { return []; } } public function childExists($name) { try { $ret = $this->getChild($name); return $ret !== null; } catch (NotFound $ex) { return false; } catch (MethodNotAllowed $ex) { return false; } } public function delete() { throw new Forbidden('Permission denied to delete this folder'); } public function getName() { list(,$name) = Uri\split($this->principalInfo['uri']); return $name; } public function setName($name) { throw new Forbidden('Permission denied to rename this folder'); } /** * Returns the last modification time, as a unix timestamp * * @return int|null */ public function getLastModified() { return null; } } Avatars/RootCollection.php 0000604 00000001251 15247164651 0011623 0 ustar 00 <?php namespace OCA\DAV\Avatars; use Sabre\DAVACL\AbstractPrincipalCollection; class RootCollection extends AbstractPrincipalCollection { /** * This method returns a node for a principal. * * The passed array contains principal information, and is guaranteed to * at least contain a uri item. Other properties may or may not be * supplied by the authentication backend. * * @param array $principalInfo * @return AvatarHome */ public function getChildForPrincipal(array $principalInfo) { $avatarManager = \OC::$server->getAvatarManager(); return new AvatarHome($principalInfo, $avatarManager); } public function getName() { return 'avatars'; } } RootCollection.php 0000604 00000011555 15247164651 0010232 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalendarRoot; use OCA\DAV\CalDAV\PublicCalendarRoot; use OCA\DAV\CardDAV\AddressBookRoot; use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\GroupPrincipalBackend; use OCA\DAV\DAV\SystemPrincipalBackend; use Sabre\CalDAV\Principal\Collection; use Sabre\DAV\SimpleCollection; class RootCollection extends SimpleCollection { public function __construct() { $config = \OC::$server->getConfig(); $random = \OC::$server->getSecureRandom(); $userManager = \OC::$server->getUserManager(); $db = \OC::$server->getDatabaseConnection(); $dispatcher = \OC::$server->getEventDispatcher(); $userPrincipalBackend = new Principal( $userManager, \OC::$server->getGroupManager() ); $groupPrincipalBackend = new GroupPrincipalBackend( \OC::$server->getGroupManager() ); // as soon as debug mode is enabled we allow listing of principals $disableListing = !$config->getSystemValue('debug', false); // setup the first level of the dav tree $userPrincipals = new Collection($userPrincipalBackend, 'principals/users'); $userPrincipals->disableListing = $disableListing; $groupPrincipals = new Collection($groupPrincipalBackend, 'principals/groups'); $groupPrincipals->disableListing = $disableListing; $systemPrincipals = new Collection(new SystemPrincipalBackend(), 'principals/system'); $systemPrincipals->disableListing = $disableListing; $filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users'); $filesCollection->disableListing = $disableListing; $caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $random, $dispatcher); $calendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users'); $calendarRoot->disableListing = $disableListing; $publicCalendarRoot = new PublicCalendarRoot($caldavBackend); $publicCalendarRoot->disableListing = $disableListing; $systemTagCollection = new SystemTag\SystemTagsByIdCollection( \OC::$server->getSystemTagManager(), \OC::$server->getUserSession(), \OC::$server->getGroupManager() ); $systemTagRelationsCollection = new SystemTag\SystemTagsRelationsCollection( \OC::$server->getSystemTagManager(), \OC::$server->getSystemTagObjectMapper(), \OC::$server->getUserSession(), \OC::$server->getGroupManager(), \OC::$server->getEventDispatcher() ); $commentsCollection = new Comments\RootCollection( \OC::$server->getCommentsManager(), \OC::$server->getUserManager(), \OC::$server->getUserSession(), \OC::$server->getEventDispatcher(), \OC::$server->getLogger() ); $usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, \OC::$server->getUserManager(), $dispatcher); $usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, 'principals/users'); $usersAddressBookRoot->disableListing = $disableListing; $systemCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, \OC::$server->getUserManager(), $dispatcher); $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, 'principals/system'); $systemAddressBookRoot->disableListing = $disableListing; $uploadCollection = new Upload\RootCollection($userPrincipalBackend, 'principals/users'); $uploadCollection->disableListing = $disableListing; $avatarCollection = new Avatars\RootCollection($userPrincipalBackend, 'principals/users'); $avatarCollection->disableListing = $disableListing; $children = [ new SimpleCollection('principals', [ $userPrincipals, $groupPrincipals, $systemPrincipals]), $filesCollection, $calendarRoot, $publicCalendarRoot, new SimpleCollection('addressbooks', [ $usersAddressBookRoot, $systemAddressBookRoot]), $systemTagCollection, $systemTagRelationsCollection, $commentsCollection, $uploadCollection, $avatarCollection ]; parent::__construct('root', $children); } } Server.php 0000604 00000020720 15247164651 0006533 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Christoph Wurst <christoph@owncloud.com> * @author Georg Ehrke <georg@owncloud.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV; use OC\AppFramework\Utility\TimeFactory; use OCA\DAV\CalDAV\Schedule\IMipPlugin; use OCA\DAV\CardDAV\ImageExportPlugin; use OCA\DAV\CardDAV\PhotoCache; use OCA\DAV\Comments\CommentsPlugin; use OCA\DAV\Connector\Sabre\Auth; use OCA\DAV\Connector\Sabre\BearerAuth; use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin; use OCA\DAV\Connector\Sabre\CommentPropertiesPlugin; use OCA\DAV\Connector\Sabre\CopyEtagHeaderPlugin; use OCA\DAV\Connector\Sabre\DavAclPlugin; use OCA\DAV\Connector\Sabre\DummyGetResponsePlugin; use OCA\DAV\Connector\Sabre\FakeLockerPlugin; use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\FilesReportPlugin; use OCA\DAV\Connector\Sabre\SharesPlugin; use OCA\DAV\DAV\PublicAuth; use OCA\DAV\DAV\CustomPropertiesBackend; use OCA\DAV\Connector\Sabre\QuotaPlugin; use OCA\DAV\Files\BrowserErrorPagePlugin; use OCA\DAV\SystemTag\SystemTagPlugin; use OCP\IRequest; use OCP\SabrePluginEvent; use Sabre\CardDAV\VCFExportPlugin; use Sabre\DAV\Auth\Plugin; use OCA\DAV\Connector\Sabre\TagsPlugin; use Sabre\HTTP\Auth\Bearer; use SearchDAV\DAV\SearchPlugin; class Server { /** @var IRequest */ private $request; /** @var string */ private $baseUri; /** @var Connector\Sabre\Server */ private $server; public function __construct(IRequest $request, $baseUri) { $this->request = $request; $this->baseUri = $baseUri; $logger = \OC::$server->getLogger(); $mailer = \OC::$server->getMailer(); $dispatcher = \OC::$server->getEventDispatcher(); $timezone = new TimeFactory(); $root = new RootCollection(); $this->server = new \OCA\DAV\Connector\Sabre\Server($root); // Add maintenance plugin $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin(\OC::$server->getConfig())); // Backends $authBackend = new Auth( \OC::$server->getSession(), \OC::$server->getUserSession(), \OC::$server->getRequest(), \OC::$server->getTwoFactorAuthManager(), \OC::$server->getBruteForceThrottler() ); // Set URL explicitly due to reverse-proxy situations $this->server->httpRequest->setUrl($this->request->getRequestUri()); $this->server->setBaseUri($this->baseUri); $this->server->addPlugin(new BlockLegacyClientPlugin(\OC::$server->getConfig())); $authPlugin = new Plugin(); $authPlugin->addBackend(new PublicAuth()); $this->server->addPlugin($authPlugin); // allow setup of additional auth backends $event = new SabrePluginEvent($this->server); $dispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event); $bearerAuthBackend = new BearerAuth( \OC::$server->getUserSession(), \OC::$server->getSession(), \OC::$server->getRequest() ); $authPlugin->addBackend($bearerAuthBackend); // because we are throwing exceptions this plugin has to be the last one $authPlugin->addBackend($authBackend); // debugging if(\OC::$server->getConfig()->getSystemValue('debug', false)) { $this->server->addPlugin(new \Sabre\DAV\Browser\Plugin()); } else { $this->server->addPlugin(new DummyGetResponsePlugin()); } $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $logger)); $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin()); $this->server->addPlugin(new \Sabre\DAV\Sync\Plugin()); // acl $acl = new DavAclPlugin(); $acl->principalCollectionSet = [ 'principals/users', 'principals/groups' ]; $acl->defaultUsernamePath = 'principals/users'; $this->server->addPlugin($acl); // calendar plugins $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin()); $this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin()); $this->server->addPlugin(new IMipPlugin($mailer, $logger, $timezone)); $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin()); $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin()); $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest())); $this->server->addPlugin(new \OCA\DAV\CalDAV\Publishing\PublishPlugin( \OC::$server->getConfig(), \OC::$server->getURLGenerator() )); // addressbook plugins $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin()); $this->server->addPlugin(new VCFExportPlugin()); $this->server->addPlugin(new ImageExportPlugin(new PhotoCache(\OC::$server->getAppDataDir('dav-photocache')))); // system tags plugins $this->server->addPlugin(new SystemTagPlugin( \OC::$server->getSystemTagManager(), \OC::$server->getGroupManager(), \OC::$server->getUserSession() )); // comments plugin $this->server->addPlugin(new CommentsPlugin( \OC::$server->getCommentsManager(), \OC::$server->getUserSession() )); $this->server->addPlugin(new CopyEtagHeaderPlugin()); // allow setup of additional plugins $dispatcher->dispatch('OCA\DAV\Connector\Sabre::addPlugin', $event); // Some WebDAV clients do require Class 2 WebDAV support (locking), since // we do not provide locking we emulate it using a fake locking plugin. if($request->isUserAgent([ '/WebDAVFS/', '/Microsoft Office OneNote 2013/', '/^Microsoft-WebDAV/',// Microsoft-WebDAV-MiniRedir/6.1.7601 ])) { $this->server->addPlugin(new FakeLockerPlugin()); } if (BrowserErrorPagePlugin::isBrowserRequest($request)) { $this->server->addPlugin(new BrowserErrorPagePlugin()); } // wait with registering these until auth is handled and the filesystem is setup $this->server->on('beforeMethod', function () { // custom properties plugin must be the last one $userSession = \OC::$server->getUserSession(); $user = $userSession->getUser(); if ($user !== null) { $view = \OC\Files\Filesystem::getView(); $this->server->addPlugin( new FilesPlugin( $this->server->tree, \OC::$server->getConfig(), $this->request, \OC::$server->getPreviewManager(), false, !\OC::$server->getConfig()->getSystemValue('debug', false) ) ); $this->server->addPlugin( new \Sabre\DAV\PropertyStorage\Plugin( new CustomPropertiesBackend( $this->server->tree, \OC::$server->getDatabaseConnection(), \OC::$server->getUserSession()->getUser() ) ) ); if ($view !== null) { $this->server->addPlugin( new QuotaPlugin($view)); } $this->server->addPlugin( new TagsPlugin( $this->server->tree, \OC::$server->getTagManager() ) ); // TODO: switch to LazyUserFolder $userFolder = \OC::$server->getUserFolder(); $this->server->addPlugin(new SharesPlugin( $this->server->tree, $userSession, $userFolder, \OC::$server->getShareManager() )); $this->server->addPlugin(new CommentPropertiesPlugin( \OC::$server->getCommentsManager(), $userSession )); $this->server->addPlugin(new \OCA\DAV\CalDAV\Search\SearchPlugin()); if ($view !== null) { $this->server->addPlugin(new FilesReportPlugin( $this->server->tree, $view, \OC::$server->getSystemTagManager(), \OC::$server->getSystemTagObjectMapper(), \OC::$server->getTagManager(), $userSession, \OC::$server->getGroupManager(), $userFolder )); $this->server->addPlugin(new SearchPlugin(new \OCA\DAV\Files\FileSearchBackend( $this->server->tree, $user, \OC::$server->getRootFolder(), \OC::$server->getShareManager(), $view ))); } } }); } public function exec() { $this->server->exec(); } } CalDAV/CalendarObject.php 0000604 00000004645 15247164651 0011167 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2017, Georg Ehrke * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Georg Ehrke <oc.list@georgehrke.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; use Sabre\VObject\Component; use Sabre\VObject\Property; use Sabre\VObject\Reader; class CalendarObject extends \Sabre\CalDAV\CalendarObject { /** * @inheritdoc */ function get() { $data = parent::get(); if ($this->isShared() && $this->objectData['classification'] === CalDavBackend::CLASSIFICATION_CONFIDENTIAL) { return $this->createConfidentialObject($data); } return $data; } protected function isShared() { if (!isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) { return false; } return $this->calendarInfo['{http://owncloud.org/ns}owner-principal'] !== $this->calendarInfo['principaluri']; } /** * @param string $calData * @return string */ private static function createConfidentialObject($calData) { $vObject = Reader::read($calData); /** @var Component $vElement */ $vElement = null; if(isset($vObject->VEVENT)) { $vElement = $vObject->VEVENT; } if(isset($vObject->VJOURNAL)) { $vElement = $vObject->VJOURNAL; } if(isset($vObject->VTODO)) { $vElement = $vObject->VTODO; } if(!is_null($vElement)) { foreach ($vElement->children() as &$property) { /** @var Property $property */ switch($property->name) { case 'CREATED': case 'DTSTART': case 'RRULE': case 'DURATION': case 'DTEND': case 'CLASS': case 'UID': break; case 'SUMMARY': $property->setValue('Busy'); break; default: $vElement->__unset($property->name); unset($property); break; } } } return $vObject->serialize(); } } CalDAV/BirthdayService.php 0000604 00000021471 15247164651 0011412 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2016, Georg Ehrke * * @author Achim Königs <garfonso@tratschtante.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Georg Ehrke <georg@nextcloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; use Exception; use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\DAV\GroupPrincipalBackend; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Component\VCard; use Sabre\VObject\DateTimeParser; use Sabre\VObject\Document; use Sabre\VObject\InvalidDataException; use Sabre\VObject\Property\VCard\DateAndOrTime; use Sabre\VObject\Reader; class BirthdayService { const BIRTHDAY_CALENDAR_URI = 'contact_birthdays'; /** @var GroupPrincipalBackend */ private $principalBackend; /** @var CalDavBackend */ private $calDavBackEnd; /** @var CardDavBackend */ private $cardDavBackEnd; /** * BirthdayService constructor. * * @param CalDavBackend $calDavBackEnd * @param CardDavBackend $cardDavBackEnd * @param GroupPrincipalBackend $principalBackend */ public function __construct(CalDavBackend $calDavBackEnd, CardDavBackend $cardDavBackEnd, GroupPrincipalBackend $principalBackend) { $this->calDavBackEnd = $calDavBackEnd; $this->cardDavBackEnd = $cardDavBackEnd; $this->principalBackend = $principalBackend; } /** * @param int $addressBookId * @param string $cardUri * @param string $cardData */ public function onCardChanged($addressBookId, $cardUri, $cardData) { $targetPrincipals = $this->getAllAffectedPrincipals($addressBookId); $book = $this->cardDavBackEnd->getAddressBookById($addressBookId); $targetPrincipals[] = $book['principaluri']; $datesToSync = [ ['postfix' => '', 'field' => 'BDAY', 'symbol' => '*'], ['postfix' => '-death', 'field' => 'DEATHDATE', 'symbol' => "†"], ['postfix' => '-anniversary', 'field' => 'ANNIVERSARY', 'symbol' => "⚭"], ]; foreach ($targetPrincipals as $principalUri) { $calendar = $this->ensureCalendarExists($principalUri); foreach ($datesToSync as $type) { $this->updateCalendar($cardUri, $cardData, $book, $calendar['id'], $type); } } } /** * @param int $addressBookId * @param string $cardUri */ public function onCardDeleted($addressBookId, $cardUri) { $targetPrincipals = $this->getAllAffectedPrincipals($addressBookId); $book = $this->cardDavBackEnd->getAddressBookById($addressBookId); $targetPrincipals[] = $book['principaluri']; foreach ($targetPrincipals as $principalUri) { $calendar = $this->ensureCalendarExists($principalUri); foreach (['', '-death', '-anniversary'] as $tag) { $objectUri = $book['uri'] . '-' . $cardUri . $tag .'.ics'; $this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri); } } } /** * @param string $principal * @return array|null * @throws \Sabre\DAV\Exception\BadRequest */ public function ensureCalendarExists($principal) { $book = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI); if (!is_null($book)) { return $book; } $this->calDavBackEnd->createCalendar($principal, self::BIRTHDAY_CALENDAR_URI, [ '{DAV:}displayname' => 'Contact birthdays', '{http://apple.com/ns/ical/}calendar-color' => '#FFFFCA', 'components' => 'VEVENT', ]); return $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI); } /** * @param string $cardData * @param string $dateField * @param string $summarySymbol * @return null|VCalendar */ public function buildDateFromContact($cardData, $dateField, $summarySymbol) { if (empty($cardData)) { return null; } try { $doc = Reader::read($cardData); // We're always converting to vCard 4.0 so we can rely on the // VCardConverter handling the X-APPLE-OMIT-YEAR property for us. if (!$doc instanceof VCard) { return null; } $doc = $doc->convert(Document::VCARD40); } catch (Exception $e) { return null; } if (!isset($doc->{$dateField})) { return null; } if (!isset($doc->FN)) { return null; } $birthday = $doc->{$dateField}; if (!(string)$birthday) { return null; } // Skip if the BDAY property is not of the right type. if (!$birthday instanceof DateAndOrTime) { return null; } // Skip if we can't parse the BDAY value. try { $dateParts = DateTimeParser::parseVCardDateTime($birthday->getValue()); } catch (InvalidDataException $e) { return null; } $unknownYear = false; if (!$dateParts['year']) { $birthday = '1900-' . $dateParts['month'] . '-' . $dateParts['date']; $unknownYear = true; } try { $date = new \DateTime($birthday); } catch (Exception $e) { return null; } if ($unknownYear) { $summary = $doc->FN->getValue() . ' ' . $summarySymbol; } else { $year = (int)$date->format('Y'); $summary = $doc->FN->getValue() . " ($summarySymbol$year)"; } $vCal = new VCalendar(); $vCal->VERSION = '2.0'; $vEvent = $vCal->createComponent('VEVENT'); $vEvent->add('DTSTART'); $vEvent->DTSTART->setDateTime( $date ); $vEvent->DTSTART['VALUE'] = 'DATE'; $vEvent->add('DTEND'); $date->add(new \DateInterval('P1D')); $vEvent->DTEND->setDateTime( $date ); $vEvent->DTEND['VALUE'] = 'DATE'; $vEvent->{'UID'} = $doc->UID; $vEvent->{'RRULE'} = 'FREQ=YEARLY'; $vEvent->{'SUMMARY'} = $summary; $vEvent->{'TRANSP'} = 'TRANSPARENT'; $alarm = $vCal->createComponent('VALARM'); $alarm->add($vCal->createProperty('TRIGGER', '-PT0M', ['VALUE' => 'DURATION'])); $alarm->add($vCal->createProperty('ACTION', 'DISPLAY')); $alarm->add($vCal->createProperty('DESCRIPTION', $vEvent->{'SUMMARY'})); $vEvent->add($alarm); $vCal->add($vEvent); return $vCal; } /** * @param string $user */ public function syncUser($user) { $principal = 'principals/users/'.$user; $this->ensureCalendarExists($principal); $books = $this->cardDavBackEnd->getAddressBooksForUser($principal); foreach($books as $book) { $cards = $this->cardDavBackEnd->getCards($book['id']); foreach($cards as $card) { $this->onCardChanged($book['id'], $card['uri'], $card['carddata']); } } } /** * @param string $existingCalendarData * @param VCalendar $newCalendarData * @return bool */ public function birthdayEvenChanged($existingCalendarData, $newCalendarData) { try { $existingBirthday = Reader::read($existingCalendarData); } catch (Exception $ex) { return true; } if ($newCalendarData->VEVENT->DTSTART->getValue() !== $existingBirthday->VEVENT->DTSTART->getValue() || $newCalendarData->VEVENT->SUMMARY->getValue() !== $existingBirthday->VEVENT->SUMMARY->getValue() ) { return true; } return false; } /** * @param integer $addressBookId * @return mixed */ protected function getAllAffectedPrincipals($addressBookId) { $targetPrincipals = []; $shares = $this->cardDavBackEnd->getShares($addressBookId); foreach ($shares as $share) { if ($share['{http://owncloud.org/ns}group-share']) { $users = $this->principalBackend->getGroupMemberSet($share['{http://owncloud.org/ns}principal']); foreach ($users as $user) { $targetPrincipals[] = $user['uri']; } } else { $targetPrincipals[] = $share['{http://owncloud.org/ns}principal']; } } return array_values(array_unique($targetPrincipals, SORT_STRING)); } /** * @param string $cardUri * @param string $cardData * @param array $book * @param int $calendarId * @param string $type */ private function updateCalendar($cardUri, $cardData, $book, $calendarId, $type) { $objectUri = $book['uri'] . '-' . $cardUri . $type['postfix'] . '.ics'; $calendarData = $this->buildDateFromContact($cardData, $type['field'], $type['symbol']); $existing = $this->calDavBackEnd->getCalendarObject($calendarId, $objectUri); if (is_null($calendarData)) { if (!is_null($existing)) { $this->calDavBackEnd->deleteCalendarObject($calendarId, $objectUri); } } else { if (is_null($existing)) { $this->calDavBackEnd->createCalendarObject($calendarId, $objectUri, $calendarData->serialize()); } else { if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) { $this->calDavBackEnd->updateCalendarObject($calendarId, $objectUri, $calendarData->serialize()); } } } } } CalDAV/Calendar.php 0000604 00000021367 15247164651 0010040 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; use OCA\DAV\DAV\Sharing\IShareable; use OCP\IL10N; use Sabre\CalDAV\Backend\BackendInterface; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropPatch; /** * Class Calendar * * @package OCA\DAV\CalDAV * @property BackendInterface|CalDavBackend $caldavBackend */ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { public function __construct(BackendInterface $caldavBackend, $calendarInfo, IL10N $l10n) { parent::__construct($caldavBackend, $calendarInfo); if ($this->getName() === BirthdayService::BIRTHDAY_CALENDAR_URI) { $this->calendarInfo['{DAV:}displayname'] = $l10n->t('Contact birthdays'); } if ($this->getName() === CalDavBackend::PERSONAL_CALENDAR_URI && $this->calendarInfo['{DAV:}displayname'] === CalDavBackend::PERSONAL_CALENDAR_NAME) { $this->calendarInfo['{DAV:}displayname'] = $l10n->t('Personal'); } } /** * Updates the list of shares. * * The first array is a list of people that are to be added to the * resource. * * Every element in the add array has the following properties: * * href - A url. Usually a mailto: address * * commonName - Usually a first and last name, or false * * summary - A description of the share, can also be false * * readOnly - A boolean value * * Every element in the remove array is just the address string. * * @param array $add * @param array $remove * @return void * @throws Forbidden */ public function updateShares(array $add, array $remove) { if ($this->isShared()) { throw new Forbidden(); } $this->caldavBackend->updateShares($this, $add, $remove); } /** * Returns the list of people whom this resource is shared with. * * Every element in this array should have the following properties: * * href - Often a mailto: address * * commonName - Optional, for example a first + last name * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. * * readOnly - boolean * * summary - Optional, a description for the share * * @return array */ public function getShares() { if ($this->isShared()) { return []; } return $this->caldavBackend->getShares($this->getResourceId()); } /** * @return int */ public function getResourceId() { return $this->calendarInfo['id']; } /** * @return string */ public function getPrincipalURI() { return $this->calendarInfo['principaluri']; } public function getACL() { $acl = [ [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ]]; if ($this->getName() !== BirthdayService::BIRTHDAY_CALENDAR_URI) { $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner(), 'protected' => true, ]; } else { $acl[] = [ 'privilege' => '{DAV:}write-properties', 'principal' => $this->getOwner(), 'protected' => true, ]; } if ($this->getOwner() !== parent::getOwner()) { $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => parent::getOwner(), 'protected' => true, ]; if ($this->canWrite()) { $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => parent::getOwner(), 'protected' => true, ]; } else { $acl[] = [ 'privilege' => '{DAV:}write-properties', 'principal' => parent::getOwner(), 'protected' => true, ]; } } if ($this->isPublic()) { $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => 'principals/system/public', 'protected' => true, ]; } $acl = $this->caldavBackend->applyShareAcl($this->getResourceId(), $acl); if (!$this->isShared()) { return $acl; } $allowedPrincipals = [$this->getOwner(), parent::getOwner(), 'principals/system/public']; return array_filter($acl, function($rule) use ($allowedPrincipals) { return in_array($rule['principal'], $allowedPrincipals); }); } public function getChildACL() { return $this->getACL(); } public function getOwner() { if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) { return $this->calendarInfo['{http://owncloud.org/ns}owner-principal']; } return parent::getOwner(); } public function delete() { if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']) && $this->calendarInfo['{http://owncloud.org/ns}owner-principal'] !== $this->calendarInfo['principaluri']) { $principal = 'principal:' . parent::getOwner(); $shares = $this->caldavBackend->getShares($this->getResourceId()); $shares = array_filter($shares, function($share) use ($principal){ return $share['href'] === $principal; }); if (empty($shares)) { throw new Forbidden(); } $this->caldavBackend->updateShares($this, [], [ 'href' => $principal ]); return; } parent::delete(); } public function propPatch(PropPatch $propPatch) { // parent::propPatch will only update calendars table // if calendar is shared, changes have to be made to the properties table if (!$this->isShared()) { parent::propPatch($propPatch); } } public function getChild($name) { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); if (!$obj) { throw new NotFound('Calendar object not found'); } if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) { throw new NotFound('Calendar object not found'); } $obj['acl'] = $this->getChildACL(); return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); } public function getChildren() { $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); $children = []; foreach ($objs as $obj) { if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) { continue; } $obj['acl'] = $this->getChildACL(); $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); } return $children; } public function getMultipleChildren(array $paths) { $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths); $children = []; foreach ($objs as $obj) { if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) { continue; } $obj['acl'] = $this->getChildACL(); $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); } return $children; } public function childExists($name) { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); if (!$obj) { return false; } if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) { return false; } return true; } public function calendarQuery(array $filters) { $uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); if ($this->isShared()) { return array_filter($uris, function ($uri) { return $this->childExists($uri); }); } return $uris; } /** * @param boolean $value * @return string|null */ public function setPublishStatus($value) { $publicUri = $this->caldavBackend->setPublishStatus($value, $this); $this->calendarInfo['publicuri'] = $publicUri; return $publicUri; } /** * @return mixed $value */ public function getPublishStatus() { return $this->caldavBackend->getPublishStatus($this); } private function canWrite() { if (isset($this->calendarInfo['{http://owncloud.org/ns}read-only'])) { return !$this->calendarInfo['{http://owncloud.org/ns}read-only']; } return true; } private function isPublic() { return isset($this->calendarInfo['{http://owncloud.org/ns}public']); } protected function isShared() { if (!isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) { return false; } return $this->calendarInfo['{http://owncloud.org/ns}owner-principal'] !== $this->calendarInfo['principaluri']; } public function isSubscription() { return isset($this->calendarInfo['{http://calendarserver.org/ns/}source']); } } CalDAV/CalDavBackend.php 0000604 00000214211 15247164651 0010721 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2017 Georg Ehrke * * @author Joas Schilling <coding@schilljs.com> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Citharel <tcit@tcit.fr> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Georg Ehrke <oc.list@georgehrke.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; use OCA\DAV\DAV\Sharing\IShareable; use OCP\DB\QueryBuilder\IQueryBuilder; use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\Sharing\Backend; use OCP\IDBConnection; use OCP\IUser; use OCP\IUserManager; use OCP\Security\ISecureRandom; use Sabre\CalDAV\Backend\AbstractBackend; use Sabre\CalDAV\Backend\SchedulingSupport; use Sabre\CalDAV\Backend\SubscriptionSupport; use Sabre\CalDAV\Backend\SyncSupport; use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp; use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; use Sabre\DAV; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropPatch; use Sabre\HTTP\URLUtil; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\DateTimeParser; use Sabre\VObject\Reader; use Sabre\VObject\Recur\EventIterator; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; /** * Class CalDavBackend * * Code is heavily inspired by https://github.com/fruux/sabre-dav/blob/master/lib/CalDAV/Backend/PDO.php * * @package OCA\DAV\CalDAV */ class CalDavBackend extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport { const PERSONAL_CALENDAR_URI = 'personal'; const PERSONAL_CALENDAR_NAME = 'Personal'; /** * We need to specify a max date, because we need to stop *somewhere* * * On 32 bit system the maximum for a signed integer is 2147483647, so * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results * in 2038-01-19 to avoid problems when the date is converted * to a unix timestamp. */ const MAX_DATE = '2038-01-01'; const ACCESS_PUBLIC = 4; const CLASSIFICATION_PUBLIC = 0; const CLASSIFICATION_PRIVATE = 1; const CLASSIFICATION_CONFIDENTIAL = 2; /** * List of CalDAV properties, and how they map to database field names * Add your own properties by simply adding on to this array. * * Note that only string-based properties are supported here. * * @var array */ public $propertyMap = [ '{DAV:}displayname' => 'displayname', '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', ]; /** * List of subscription properties, and how they map to database field names. * * @var array */ public $subscriptionPropertyMap = [ '{DAV:}displayname' => 'displayname', '{http://apple.com/ns/ical/}refreshrate' => 'refreshrate', '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', '{http://calendarserver.org/ns/}subscribed-strip-todos' => 'striptodos', '{http://calendarserver.org/ns/}subscribed-strip-alarms' => 'stripalarms', '{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments', ]; /** @var array properties to index */ public static $indexProperties = ['CATEGORIES', 'COMMENT', 'DESCRIPTION', 'LOCATION', 'RESOURCES', 'STATUS', 'SUMMARY', 'ATTENDEE', 'CONTACT', 'ORGANIZER']; /** @var array parameters to index */ public static $indexParameters = [ 'ATTENDEE' => ['CN'], 'ORGANIZER' => ['CN'], ]; /** * @var string[] Map of uid => display name */ protected $userDisplayNames; /** @var IDBConnection */ private $db; /** @var Backend */ private $sharingBackend; /** @var Principal */ private $principalBackend; /** @var IUserManager */ private $userManager; /** @var ISecureRandom */ private $random; /** @var EventDispatcherInterface */ private $dispatcher; /** @var bool */ private $legacyEndpoint; /** @var string */ private $dbObjectPropertiesTable = 'calendarobjects_props'; /** * CalDavBackend constructor. * * @param IDBConnection $db * @param Principal $principalBackend * @param IUserManager $userManager * @param ISecureRandom $random * @param EventDispatcherInterface $dispatcher * @param bool $legacyEndpoint */ public function __construct(IDBConnection $db, Principal $principalBackend, IUserManager $userManager, ISecureRandom $random, EventDispatcherInterface $dispatcher, $legacyEndpoint = false) { $this->db = $db; $this->principalBackend = $principalBackend; $this->userManager = $userManager; $this->sharingBackend = new Backend($this->db, $principalBackend, 'calendar'); $this->random = $random; $this->dispatcher = $dispatcher; $this->legacyEndpoint = $legacyEndpoint; } /** * Return the number of calendars for a principal * * By default this excludes the automatically generated birthday calendar * * @param $principalUri * @param bool $excludeBirthday * @return int */ public function getCalendarsForUserCount($principalUri, $excludeBirthday = true) { $principalUri = $this->convertPrincipal($principalUri, true); $query = $this->db->getQueryBuilder(); $query->select($query->createFunction('COUNT(*)')) ->from('calendars') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))); if ($excludeBirthday) { $query->andWhere($query->expr()->neq('uri', $query->createNamedParameter(BirthdayService::BIRTHDAY_CALENDAR_URI))); } return (int)$query->execute()->fetchColumn(); } /** * Returns a list of calendars for a principal. * * Every project is an array with the following keys: * * id, a unique id that will be used by other functions to modify the * calendar. This can be the same as the uri or a database key. * * uri, which the basename of the uri with which the calendar is * accessed. * * principaluri. The owner of the calendar. Almost always the same as * principalUri passed to this method. * * Furthermore it can contain webdav properties in clark notation. A very * common one is '{DAV:}displayname'. * * Many clients also require: * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set * For this property, you can just return an instance of * Sabre\CalDAV\Property\SupportedCalendarComponentSet. * * If you return {http://sabredav.org/ns}read-only and set the value to 1, * ACL will automatically be put in read-only mode. * * @param string $principalUri * @return array */ function getCalendarsForUser($principalUri) { $principalUriOriginal = $principalUri; $principalUri = $this->convertPrincipal($principalUri, true); $fields = array_values($this->propertyMap); $fields[] = 'id'; $fields[] = 'uri'; $fields[] = 'synctoken'; $fields[] = 'components'; $fields[] = 'principaluri'; $fields[] = 'transparent'; // Making fields a comma-delimited list $query = $this->db->getQueryBuilder(); $query->select($fields)->from('calendars') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))) ->orderBy('calendarorder', 'ASC'); $stmt = $query->execute(); $calendars = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $components = []; if ($row['components']) { $components = explode(',',$row['components']); } $calendar = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint), '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components), '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($principalUri, !$this->legacyEndpoint), ]; foreach($this->propertyMap as $xmlName=>$dbName) { $calendar[$xmlName] = $row[$dbName]; } $this->addOwnerPrincipal($calendar); if (!isset($calendars[$calendar['id']])) { $calendars[$calendar['id']] = $calendar; } } $stmt->closeCursor(); // query for shared calendars $principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true); $principals = array_map(function($principal) { return urldecode($principal); }, $principals); $principals[]= $principalUri; $fields = array_values($this->propertyMap); $fields[] = 'a.id'; $fields[] = 'a.uri'; $fields[] = 'a.synctoken'; $fields[] = 'a.components'; $fields[] = 'a.principaluri'; $fields[] = 'a.transparent'; $fields[] = 's.access'; $query = $this->db->getQueryBuilder(); $result = $query->select($fields) ->from('dav_shares', 's') ->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id')) ->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri'))) ->andWhere($query->expr()->eq('s.type', $query->createParameter('type'))) ->setParameter('type', 'calendar') ->setParameter('principaluri', $principals, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY) ->execute(); $readOnlyPropertyName = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only'; while($row = $result->fetch()) { if ($row['principaluri'] === $principalUri) { continue; } $readOnly = (int) $row['access'] === Backend::ACCESS_READ; if (isset($calendars[$row['id']])) { if ($readOnly) { // New share can not have more permissions then the old one. continue; } if (isset($calendars[$row['id']][$readOnlyPropertyName]) && $calendars[$row['id']][$readOnlyPropertyName] === 0) { // Old share is already read-write, no more permissions can be gained continue; } } list(, $name) = URLUtil::splitPath($row['principaluri']); $uri = $row['uri'] . '_shared_by_' . $name; $row['displayname'] = $row['displayname'] . ' (' . $this->getUserDisplayName($name) . ')'; $components = []; if ($row['components']) { $components = explode(',',$row['components']); } $calendar = [ 'id' => $row['id'], 'uri' => $uri, 'principaluri' => $this->convertPrincipal($principalUri, !$this->legacyEndpoint), '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components), '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint), $readOnlyPropertyName => $readOnly, ]; foreach($this->propertyMap as $xmlName=>$dbName) { $calendar[$xmlName] = $row[$dbName]; } $this->addOwnerPrincipal($calendar); $calendars[$calendar['id']] = $calendar; } $result->closeCursor(); return array_values($calendars); } public function getUsersOwnCalendars($principalUri) { $principalUri = $this->convertPrincipal($principalUri, true); $fields = array_values($this->propertyMap); $fields[] = 'id'; $fields[] = 'uri'; $fields[] = 'synctoken'; $fields[] = 'components'; $fields[] = 'principaluri'; $fields[] = 'transparent'; // Making fields a comma-delimited list $query = $this->db->getQueryBuilder(); $query->select($fields)->from('calendars') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))) ->orderBy('calendarorder', 'ASC'); $stmt = $query->execute(); $calendars = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $components = []; if ($row['components']) { $components = explode(',',$row['components']); } $calendar = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint), '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components), '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), ]; foreach($this->propertyMap as $xmlName=>$dbName) { $calendar[$xmlName] = $row[$dbName]; } $this->addOwnerPrincipal($calendar); if (!isset($calendars[$calendar['id']])) { $calendars[$calendar['id']] = $calendar; } } $stmt->closeCursor(); return array_values($calendars); } private function getUserDisplayName($uid) { if (!isset($this->userDisplayNames[$uid])) { $user = $this->userManager->get($uid); if ($user instanceof IUser) { $this->userDisplayNames[$uid] = $user->getDisplayName(); } else { $this->userDisplayNames[$uid] = $uid; } } return $this->userDisplayNames[$uid]; } /** * @return array */ public function getPublicCalendars() { $fields = array_values($this->propertyMap); $fields[] = 'a.id'; $fields[] = 'a.uri'; $fields[] = 'a.synctoken'; $fields[] = 'a.components'; $fields[] = 'a.principaluri'; $fields[] = 'a.transparent'; $fields[] = 's.access'; $fields[] = 's.publicuri'; $calendars = []; $query = $this->db->getQueryBuilder(); $result = $query->select($fields) ->from('dav_shares', 's') ->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id')) ->where($query->expr()->in('s.access', $query->createNamedParameter(self::ACCESS_PUBLIC))) ->andWhere($query->expr()->eq('s.type', $query->createNamedParameter('calendar'))) ->execute(); while($row = $result->fetch()) { list(, $name) = URLUtil::splitPath($row['principaluri']); $row['displayname'] = $row['displayname'] . "($name)"; $components = []; if ($row['components']) { $components = explode(',',$row['components']); } $calendar = [ 'id' => $row['id'], 'uri' => $row['publicuri'], 'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint), '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components), '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], $this->legacyEndpoint), '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ, '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}public' => (int)$row['access'] === self::ACCESS_PUBLIC, ]; foreach($this->propertyMap as $xmlName=>$dbName) { $calendar[$xmlName] = $row[$dbName]; } $this->addOwnerPrincipal($calendar); if (!isset($calendars[$calendar['id']])) { $calendars[$calendar['id']] = $calendar; } } $result->closeCursor(); return array_values($calendars); } /** * @param string $uri * @return array * @throws NotFound */ public function getPublicCalendar($uri) { $fields = array_values($this->propertyMap); $fields[] = 'a.id'; $fields[] = 'a.uri'; $fields[] = 'a.synctoken'; $fields[] = 'a.components'; $fields[] = 'a.principaluri'; $fields[] = 'a.transparent'; $fields[] = 's.access'; $fields[] = 's.publicuri'; $query = $this->db->getQueryBuilder(); $result = $query->select($fields) ->from('dav_shares', 's') ->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id')) ->where($query->expr()->in('s.access', $query->createNamedParameter(self::ACCESS_PUBLIC))) ->andWhere($query->expr()->eq('s.type', $query->createNamedParameter('calendar'))) ->andWhere($query->expr()->eq('s.publicuri', $query->createNamedParameter($uri))) ->execute(); $row = $result->fetch(\PDO::FETCH_ASSOC); $result->closeCursor(); if ($row === false) { throw new NotFound('Node with name \'' . $uri . '\' could not be found'); } list(, $name) = URLUtil::splitPath($row['principaluri']); $row['displayname'] = $row['displayname'] . ' ' . "($name)"; $components = []; if ($row['components']) { $components = explode(',',$row['components']); } $calendar = [ 'id' => $row['id'], 'uri' => $row['publicuri'], 'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint), '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components), '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint), '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ, '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}public' => (int)$row['access'] === self::ACCESS_PUBLIC, ]; foreach($this->propertyMap as $xmlName=>$dbName) { $calendar[$xmlName] = $row[$dbName]; } $this->addOwnerPrincipal($calendar); return $calendar; } /** * @param string $principal * @param string $uri * @return array|null */ public function getCalendarByUri($principal, $uri) { $fields = array_values($this->propertyMap); $fields[] = 'id'; $fields[] = 'uri'; $fields[] = 'synctoken'; $fields[] = 'components'; $fields[] = 'principaluri'; $fields[] = 'transparent'; // Making fields a comma-delimited list $query = $this->db->getQueryBuilder(); $query->select($fields)->from('calendars') ->where($query->expr()->eq('uri', $query->createNamedParameter($uri))) ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) ->setMaxResults(1); $stmt = $query->execute(); $row = $stmt->fetch(\PDO::FETCH_ASSOC); $stmt->closeCursor(); if ($row === false) { return null; } $components = []; if ($row['components']) { $components = explode(',',$row['components']); } $calendar = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint), '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components), '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), ]; foreach($this->propertyMap as $xmlName=>$dbName) { $calendar[$xmlName] = $row[$dbName]; } $this->addOwnerPrincipal($calendar); return $calendar; } public function getCalendarById($calendarId) { $fields = array_values($this->propertyMap); $fields[] = 'id'; $fields[] = 'uri'; $fields[] = 'synctoken'; $fields[] = 'components'; $fields[] = 'principaluri'; $fields[] = 'transparent'; // Making fields a comma-delimited list $query = $this->db->getQueryBuilder(); $query->select($fields)->from('calendars') ->where($query->expr()->eq('id', $query->createNamedParameter($calendarId))) ->setMaxResults(1); $stmt = $query->execute(); $row = $stmt->fetch(\PDO::FETCH_ASSOC); $stmt->closeCursor(); if ($row === false) { return null; } $components = []; if ($row['components']) { $components = explode(',',$row['components']); } $calendar = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint), '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components), '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), ]; foreach($this->propertyMap as $xmlName=>$dbName) { $calendar[$xmlName] = $row[$dbName]; } $this->addOwnerPrincipal($calendar); return $calendar; } /** * Creates a new calendar for a principal. * * If the creation was a success, an id must be returned that can be used to reference * this calendar in other methods, such as updateCalendar. * * @param string $principalUri * @param string $calendarUri * @param array $properties * @return int */ function createCalendar($principalUri, $calendarUri, array $properties) { $values = [ 'principaluri' => $this->convertPrincipal($principalUri, true), 'uri' => $calendarUri, 'synctoken' => 1, 'transparent' => 0, 'components' => 'VEVENT,VTODO', 'displayname' => $calendarUri ]; // Default value $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; if (isset($properties[$sccs])) { if (!($properties[$sccs] instanceof SupportedCalendarComponentSet)) { throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Property\SupportedCalendarComponentSet'); } $values['components'] = implode(',',$properties[$sccs]->getValue()); } $transp = '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp'; if (isset($properties[$transp])) { $values['transparent'] = (int) ($properties[$transp]->getValue() === 'transparent'); } foreach($this->propertyMap as $xmlName=>$dbName) { if (isset($properties[$xmlName])) { $values[$dbName] = $properties[$xmlName]; } } $query = $this->db->getQueryBuilder(); $query->insert('calendars'); foreach($values as $column => $value) { $query->setValue($column, $query->createNamedParameter($value)); } $query->execute(); $calendarId = $query->getLastInsertId(); $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCalendar', new GenericEvent( '\OCA\DAV\CalDAV\CalDavBackend::createCalendar', [ 'calendarId' => $calendarId, 'calendarData' => $this->getCalendarById($calendarId), ])); return $calendarId; } /** * Updates properties for a calendar. * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documentation for more info and examples. * * @param PropPatch $propPatch * @return void */ function updateCalendar($calendarId, PropPatch $propPatch) { $supportedProperties = array_keys($this->propertyMap); $supportedProperties[] = '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp'; $propPatch->handle($supportedProperties, function($mutations) use ($calendarId) { $newValues = []; foreach ($mutations as $propertyName => $propertyValue) { switch ($propertyName) { case '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' : $fieldName = 'transparent'; $newValues[$fieldName] = (int) ($propertyValue->getValue() === 'transparent'); break; default : $fieldName = $this->propertyMap[$propertyName]; $newValues[$fieldName] = $propertyValue; break; } } $query = $this->db->getQueryBuilder(); $query->update('calendars'); foreach ($newValues as $fieldName => $value) { $query->set($fieldName, $query->createNamedParameter($value)); } $query->where($query->expr()->eq('id', $query->createNamedParameter($calendarId))); $query->execute(); $this->addChange($calendarId, "", 2); $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCalendar', new GenericEvent( '\OCA\DAV\CalDAV\CalDavBackend::updateCalendar', [ 'calendarId' => $calendarId, 'calendarData' => $this->getCalendarById($calendarId), 'shares' => $this->getShares($calendarId), 'propertyMutations' => $mutations, ])); return true; }); } /** * Delete a calendar and all it's objects * * @param mixed $calendarId * @return void */ function deleteCalendar($calendarId) { $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', new GenericEvent( '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', [ 'calendarId' => $calendarId, 'calendarData' => $this->getCalendarById($calendarId), 'shares' => $this->getShares($calendarId), ])); $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?'); $stmt->execute([$calendarId]); $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendars` WHERE `id` = ?'); $stmt->execute([$calendarId]); $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarchanges` WHERE `calendarid` = ?'); $stmt->execute([$calendarId]); $this->sharingBackend->deleteAllShares($calendarId); $query = $this->db->getQueryBuilder(); $query->delete($this->dbObjectPropertiesTable) ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))) ->execute(); } /** * Delete all of an user's shares * * @param string $principaluri * @return void */ function deleteAllSharesByUser($principaluri) { $this->sharingBackend->deleteAllSharesByUser($principaluri); } /** * Returns all calendar objects within a calendar. * * Every item contains an array with the following keys: * * calendardata - The iCalendar-compatible calendar data * * uri - a unique key which will be used to construct the uri. This can * be any arbitrary string, but making sure it ends with '.ics' is a * good idea. This is only the basename, or filename, not the full * path. * * lastmodified - a timestamp of the last modification time * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: * '"abcdef"') * * size - The size of the calendar objects, in bytes. * * component - optional, a string containing the type of object, such * as 'vevent' or 'vtodo'. If specified, this will be used to populate * the Content-Type header. * * Note that the etag is optional, but it's highly encouraged to return for * speed reasons. * * The calendardata is also optional. If it's not returned * 'getCalendarObject' will be called later, which *is* expected to return * calendardata. * * If neither etag or size are specified, the calendardata will be * used/fetched to determine these numbers. If both are specified the * amount of times this is needed is reduced by a great degree. * * @param mixed $calendarId * @return array */ function getCalendarObjects($calendarId) { $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'componenttype', 'classification']) ->from('calendarobjects') ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))); $stmt = $query->execute(); $result = []; foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { $result[] = [ 'id' => $row['id'], 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'component' => strtolower($row['componenttype']), 'classification'=> (int)$row['classification'] ]; } return $result; } /** * Returns information from a single calendar object, based on it's object * uri. * * The object uri is only the basename, or filename and not a full path. * * The returned array must have the same keys as getCalendarObjects. The * 'calendardata' object is required here though, while it's not required * for getCalendarObjects. * * This method must return null if the object did not exist. * * @param mixed $calendarId * @param string $objectUri * @return array|null */ function getCalendarObject($calendarId, $objectUri) { $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification']) ->from('calendarobjects') ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri))); $stmt = $query->execute(); $row = $stmt->fetch(\PDO::FETCH_ASSOC); if(!$row) return null; return [ 'id' => $row['id'], 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'calendardata' => $this->readBlob($row['calendardata']), 'component' => strtolower($row['componenttype']), 'classification'=> (int)$row['classification'] ]; } /** * Returns a list of calendar objects. * * This method should work identical to getCalendarObject, but instead * return all the calendar objects in the list as an array. * * If the backend supports this, it may allow for some speed-ups. * * @param mixed $calendarId * @param string[] $uris * @return array */ function getMultipleCalendarObjects($calendarId, array $uris) { if (empty($uris)) { return []; } $chunks = array_chunk($uris, 100); $objects = []; $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification']) ->from('calendarobjects') ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))) ->andWhere($query->expr()->in('uri', $query->createParameter('uri'))); foreach ($chunks as $uris) { $query->setParameter('uri', $uris, IQueryBuilder::PARAM_STR_ARRAY); $result = $query->execute(); while ($row = $result->fetch()) { $objects[] = [ 'id' => $row['id'], 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'calendardata' => $this->readBlob($row['calendardata']), 'component' => strtolower($row['componenttype']), 'classification' => (int)$row['classification'] ]; } $result->closeCursor(); } return $objects; } /** * Creates a new calendar object. * * The object uri is only the basename, or filename and not a full path. * * It is possible return an etag from this function, which will be used in * the response to this PUT request. Note that the ETag must be surrounded * by double-quotes. * * However, you should only really return this ETag if you don't mangle the * calendar-data. If the result of a subsequent GET to this object is not * the exact same as this request body, you should omit the ETag. * * @param mixed $calendarId * @param string $objectUri * @param string $calendarData * @return string */ function createCalendarObject($calendarId, $objectUri, $calendarData) { $extraData = $this->getDenormalizedData($calendarData); $query = $this->db->getQueryBuilder(); $query->insert('calendarobjects') ->values([ 'calendarid' => $query->createNamedParameter($calendarId), 'uri' => $query->createNamedParameter($objectUri), 'calendardata' => $query->createNamedParameter($calendarData, IQueryBuilder::PARAM_LOB), 'lastmodified' => $query->createNamedParameter(time()), 'etag' => $query->createNamedParameter($extraData['etag']), 'size' => $query->createNamedParameter($extraData['size']), 'componenttype' => $query->createNamedParameter($extraData['componentType']), 'firstoccurence' => $query->createNamedParameter($extraData['firstOccurence']), 'lastoccurence' => $query->createNamedParameter($extraData['lastOccurence']), 'classification' => $query->createNamedParameter($extraData['classification']), 'uid' => $query->createNamedParameter($extraData['uid']), ]) ->execute(); $this->updateProperties($calendarId, $objectUri, $calendarData); $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', new GenericEvent( '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', [ 'calendarId' => $calendarId, 'calendarData' => $this->getCalendarById($calendarId), 'shares' => $this->getShares($calendarId), 'objectData' => $this->getCalendarObject($calendarId, $objectUri), ] )); $this->addChange($calendarId, $objectUri, 1); return '"' . $extraData['etag'] . '"'; } /** * Updates an existing calendarobject, based on it's uri. * * The object uri is only the basename, or filename and not a full path. * * It is possible return an etag from this function, which will be used in * the response to this PUT request. Note that the ETag must be surrounded * by double-quotes. * * However, you should only really return this ETag if you don't mangle the * calendar-data. If the result of a subsequent GET to this object is not * the exact same as this request body, you should omit the ETag. * * @param mixed $calendarId * @param string $objectUri * @param string $calendarData * @return string */ function updateCalendarObject($calendarId, $objectUri, $calendarData) { $extraData = $this->getDenormalizedData($calendarData); $query = $this->db->getQueryBuilder(); $query->update('calendarobjects') ->set('calendardata', $query->createNamedParameter($calendarData, IQueryBuilder::PARAM_LOB)) ->set('lastmodified', $query->createNamedParameter(time())) ->set('etag', $query->createNamedParameter($extraData['etag'])) ->set('size', $query->createNamedParameter($extraData['size'])) ->set('componenttype', $query->createNamedParameter($extraData['componentType'])) ->set('firstoccurence', $query->createNamedParameter($extraData['firstOccurence'])) ->set('lastoccurence', $query->createNamedParameter($extraData['lastOccurence'])) ->set('classification', $query->createNamedParameter($extraData['classification'])) ->set('uid', $query->createNamedParameter($extraData['uid'])) ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri))) ->execute(); $this->updateProperties($calendarId, $objectUri, $calendarData); $data = $this->getCalendarObject($calendarId, $objectUri); if (is_array($data)) { $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', new GenericEvent( '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', [ 'calendarId' => $calendarId, 'calendarData' => $this->getCalendarById($calendarId), 'shares' => $this->getShares($calendarId), 'objectData' => $data, ] )); } $this->addChange($calendarId, $objectUri, 2); return '"' . $extraData['etag'] . '"'; } /** * @param int $calendarObjectId * @param int $classification */ public function setClassification($calendarObjectId, $classification) { if (!in_array($classification, [ self::CLASSIFICATION_PUBLIC, self::CLASSIFICATION_PRIVATE, self::CLASSIFICATION_CONFIDENTIAL ])) { throw new \InvalidArgumentException(); } $query = $this->db->getQueryBuilder(); $query->update('calendarobjects') ->set('classification', $query->createNamedParameter($classification)) ->where($query->expr()->eq('id', $query->createNamedParameter($calendarObjectId))) ->execute(); } /** * Deletes an existing calendar object. * * The object uri is only the basename, or filename and not a full path. * * @param mixed $calendarId * @param string $objectUri * @return void */ function deleteCalendarObject($calendarId, $objectUri) { $data = $this->getCalendarObject($calendarId, $objectUri); if (is_array($data)) { $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', new GenericEvent( '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', [ 'calendarId' => $calendarId, 'calendarData' => $this->getCalendarById($calendarId), 'shares' => $this->getShares($calendarId), 'objectData' => $data, ] )); } $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ? AND `uri` = ?'); $stmt->execute([$calendarId, $objectUri]); $this->purgeProperties($calendarId, $data['id']); $this->addChange($calendarId, $objectUri, 3); } /** * Performs a calendar-query on the contents of this calendar. * * The calendar-query is defined in RFC4791 : CalDAV. Using the * calendar-query it is possible for a client to request a specific set of * object, based on contents of iCalendar properties, date-ranges and * iCalendar component types (VTODO, VEVENT). * * This method should just return a list of (relative) urls that match this * query. * * The list of filters are specified as an array. The exact array is * documented by Sabre\CalDAV\CalendarQueryParser. * * Note that it is extremely likely that getCalendarObject for every path * returned from this method will be called almost immediately after. You * may want to anticipate this to speed up these requests. * * This method provides a default implementation, which parses *all* the * iCalendar objects in the specified calendar. * * This default may well be good enough for personal use, and calendars * that aren't very large. But if you anticipate high usage, big calendars * or high loads, you are strongly advised to optimize certain paths. * * The best way to do so is override this method and to optimize * specifically for 'common filters'. * * Requests that are extremely common are: * * requests for just VEVENTS * * requests for just VTODO * * requests with a time-range-filter on either VEVENT or VTODO. * * ..and combinations of these requests. It may not be worth it to try to * handle every possible situation and just rely on the (relatively * easy to use) CalendarQueryValidator to handle the rest. * * Note that especially time-range-filters may be difficult to parse. A * time-range filter specified on a VEVENT must for instance also handle * recurrence rules correctly. * A good example of how to interprete all these filters can also simply * be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct * as possible, so it gives you a good idea on what type of stuff you need * to think of. * * @param mixed $calendarId * @param array $filters * @return array */ function calendarQuery($calendarId, array $filters) { $componentType = null; $requirePostFilter = true; $timeRange = null; // if no filters were specified, we don't need to filter after a query if (!$filters['prop-filters'] && !$filters['comp-filters']) { $requirePostFilter = false; } // Figuring out if there's a component filter if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) { $componentType = $filters['comp-filters'][0]['name']; // Checking if we need post-filters if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) { $requirePostFilter = false; } // There was a time-range filter if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) { $timeRange = $filters['comp-filters'][0]['time-range']; // If start time OR the end time is not specified, we can do a // 100% accurate mysql query. if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) { $requirePostFilter = false; } } } $columns = ['uri']; if ($requirePostFilter) { $columns = ['uri', 'calendardata']; } $query = $this->db->getQueryBuilder(); $query->select($columns) ->from('calendarobjects') ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))); if ($componentType) { $query->andWhere($query->expr()->eq('componenttype', $query->createNamedParameter($componentType))); } if ($timeRange && $timeRange['start']) { $query->andWhere($query->expr()->gt('lastoccurence', $query->createNamedParameter($timeRange['start']->getTimeStamp()))); } if ($timeRange && $timeRange['end']) { $query->andWhere($query->expr()->lt('firstoccurence', $query->createNamedParameter($timeRange['end']->getTimeStamp()))); } $stmt = $query->execute(); $result = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { if ($requirePostFilter) { if (!$this->validateFilterForObject($row, $filters)) { continue; } } $result[] = $row['uri']; } return $result; } /** * custom Nextcloud search extension for CalDAV * * @param string $principalUri * @param array $filters * @param integer|null $limit * @param integer|null $offset * @return array */ public function calendarSearch($principalUri, array $filters, $limit=null, $offset=null) { $calendars = $this->getCalendarsForUser($principalUri); $ownCalendars = []; $sharedCalendars = []; $uriMapper = []; foreach($calendars as $calendar) { if ($calendar['{http://owncloud.org/ns}owner-principal'] === $principalUri) { $ownCalendars[] = $calendar['id']; } else { $sharedCalendars[] = $calendar['id']; } $uriMapper[$calendar['id']] = $calendar['uri']; } if (count($ownCalendars) === 0 && count($sharedCalendars) === 0) { return []; } $query = $this->db->getQueryBuilder(); // Calendar id expressions $calendarExpressions = []; foreach($ownCalendars as $id) { $calendarExpressions[] = $query->expr() ->eq('c.calendarid', $query->createNamedParameter($id)); } foreach($sharedCalendars as $id) { $calendarExpressions[] = $query->expr()->andX( $query->expr()->eq('c.calendarid', $query->createNamedParameter($id)), $query->expr()->eq('c.classification', $query->createNamedParameter(self::CLASSIFICATION_PUBLIC)) ); } if (count($calendarExpressions) === 1) { $calExpr = $calendarExpressions[0]; } else { $calExpr = call_user_func_array([$query->expr(), 'orX'], $calendarExpressions); } // Component expressions $compExpressions = []; foreach($filters['comps'] as $comp) { $compExpressions[] = $query->expr() ->eq('c.componenttype', $query->createNamedParameter($comp)); } if (count($compExpressions) === 1) { $compExpr = $compExpressions[0]; } else { $compExpr = call_user_func_array([$query->expr(), 'orX'], $compExpressions); } if (!isset($filters['props'])) { $filters['props'] = []; } if (!isset($filters['params'])) { $filters['params'] = []; } $propParamExpressions = []; foreach($filters['props'] as $prop) { $propParamExpressions[] = $query->expr()->andX( $query->expr()->eq('i.name', $query->createNamedParameter($prop)), $query->expr()->isNull('i.parameter') ); } foreach($filters['params'] as $param) { $propParamExpressions[] = $query->expr()->andX( $query->expr()->eq('i.name', $query->createNamedParameter($param['property'])), $query->expr()->eq('i.parameter', $query->createNamedParameter($param['parameter'])) ); } if (count($propParamExpressions) === 1) { $propParamExpr = $propParamExpressions[0]; } else { $propParamExpr = call_user_func_array([$query->expr(), 'orX'], $propParamExpressions); } $query->select(['c.calendarid', 'c.uri']) ->from($this->dbObjectPropertiesTable, 'i') ->join('i', 'calendarobjects', 'c', $query->expr()->eq('i.objectid', 'c.id')) ->where($calExpr) ->andWhere($compExpr) ->andWhere($propParamExpr) ->andWhere($query->expr()->iLike('i.value', $query->createNamedParameter('%'.$this->db->escapeLikeParameter($filters['search-term']).'%'))); if ($offset) { $query->setFirstResult($offset); } if ($limit) { $query->setMaxResults($limit); } $stmt = $query->execute(); $result = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $path = $uriMapper[$row['calendarid']] . '/' . $row['uri']; if (!in_array($path, $result)) { $result[] = $path; } } return $result; } /** * Searches through all of a users calendars and calendar objects to find * an object with a specific UID. * * This method should return the path to this object, relative to the * calendar home, so this path usually only contains two parts: * * calendarpath/objectpath.ics * * If the uid is not found, return null. * * This method should only consider * objects that the principal owns, so * any calendars owned by other principals that also appear in this * collection should be ignored. * * @param string $principalUri * @param string $uid * @return string|null */ function getCalendarObjectByUID($principalUri, $uid) { $query = $this->db->getQueryBuilder(); $query->selectAlias('c.uri', 'calendaruri')->selectAlias('co.uri', 'objecturi') ->from('calendarobjects', 'co') ->leftJoin('co', 'calendars', 'c', $query->expr()->eq('co.calendarid', 'c.id')) ->where($query->expr()->eq('c.principaluri', $query->createNamedParameter($principalUri))) ->andWhere($query->expr()->eq('co.uid', $query->createNamedParameter($uid))); $stmt = $query->execute(); if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { return $row['calendaruri'] . '/' . $row['objecturi']; } return null; } /** * The getChanges method returns all the changes that have happened, since * the specified syncToken in the specified calendar. * * This function should return an array, such as the following: * * [ * 'syncToken' => 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'modified.txt', * ], * 'deleted' => [ * 'foo.php.bak', * 'old.txt' * ] * ); * * The returned syncToken property should reflect the *current* syncToken * of the calendar, as reported in the {http://sabredav.org/ns}sync-token * property This is * needed here too, to ensure the operation is atomic. * * If the $syncToken argument is specified as null, this is an initial * sync, and all members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The $syncLevel argument is basically the 'depth' of the report. If it's * 1, you only have to report changes that happened only directly in * immediate descendants. If it's 2, it should also include changes from * the nodes below the child collections. (grandchildren) * * The $limit argument allows a client to specify how many results should * be returned at most. If the limit is not specified, it should be treated * as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $calendarId * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) { // Current synctoken $stmt = $this->db->prepare('SELECT `synctoken` FROM `*PREFIX*calendars` WHERE `id` = ?'); $stmt->execute([ $calendarId ]); $currentToken = $stmt->fetchColumn(0); if (is_null($currentToken)) { return null; } $result = [ 'syncToken' => $currentToken, 'added' => [], 'modified' => [], 'deleted' => [], ]; if ($syncToken) { $query = "SELECT `uri`, `operation` FROM `*PREFIX*calendarchanges` WHERE `synctoken` >= ? AND `synctoken` < ? AND `calendarid` = ? ORDER BY `synctoken`"; if ($limit>0) { $query.= " LIMIT " . (int)$limit; } // Fetching all changes $stmt = $this->db->prepare($query); $stmt->execute([$syncToken, $currentToken, $calendarId]); $changes = []; // This loop ensures that any duplicates are overwritten, only the // last change on a node is relevant. while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $changes[$row['uri']] = $row['operation']; } foreach($changes as $uri => $operation) { switch($operation) { case 1 : $result['added'][] = $uri; break; case 2 : $result['modified'][] = $uri; break; case 3 : $result['deleted'][] = $uri; break; } } } else { // No synctoken supplied, this is the initial sync. $query = "SELECT `uri` FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?"; $stmt = $this->db->prepare($query); $stmt->execute([$calendarId]); $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN); } return $result; } /** * Returns a list of subscriptions for a principal. * * Every subscription is an array with the following keys: * * id, a unique id that will be used by other functions to modify the * subscription. This can be the same as the uri or a database key. * * uri. This is just the 'base uri' or 'filename' of the subscription. * * principaluri. The owner of the subscription. Almost always the same as * principalUri passed to this method. * * Furthermore, all the subscription info must be returned too: * * 1. {DAV:}displayname * 2. {http://apple.com/ns/ical/}refreshrate * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos * should not be stripped). * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms * should not be stripped). * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if * attachments should not be stripped). * 6. {http://calendarserver.org/ns/}source (Must be a * Sabre\DAV\Property\Href). * 7. {http://apple.com/ns/ical/}calendar-color * 8. {http://apple.com/ns/ical/}calendar-order * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set * (should just be an instance of * Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of * default components). * * @param string $principalUri * @return array */ function getSubscriptionsForUser($principalUri) { $fields = array_values($this->subscriptionPropertyMap); $fields[] = 'id'; $fields[] = 'uri'; $fields[] = 'source'; $fields[] = 'principaluri'; $fields[] = 'lastmodified'; $query = $this->db->getQueryBuilder(); $query->select($fields) ->from('calendarsubscriptions') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))) ->orderBy('calendarorder', 'asc'); $stmt =$query->execute(); $subscriptions = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $subscription = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], 'source' => $row['source'], 'lastmodified' => $row['lastmodified'], '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet(['VTODO', 'VEVENT']), ]; foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) { if (!is_null($row[$dbName])) { $subscription[$xmlName] = $row[$dbName]; } } $subscriptions[] = $subscription; } return $subscriptions; } /** * Creates a new subscription for a principal. * * If the creation was a success, an id must be returned that can be used to reference * this subscription in other methods, such as updateSubscription. * * @param string $principalUri * @param string $uri * @param array $properties * @return mixed */ function createSubscription($principalUri, $uri, array $properties) { if (!isset($properties['{http://calendarserver.org/ns/}source'])) { throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions'); } $values = [ 'principaluri' => $principalUri, 'uri' => $uri, 'source' => $properties['{http://calendarserver.org/ns/}source']->getHref(), 'lastmodified' => time(), ]; $propertiesBoolean = ['striptodos', 'stripalarms', 'stripattachments']; foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) { if (array_key_exists($xmlName, $properties)) { $values[$dbName] = $properties[$xmlName]; if (in_array($dbName, $propertiesBoolean)) { $values[$dbName] = true; } } } $valuesToInsert = array(); $query = $this->db->getQueryBuilder(); foreach (array_keys($values) as $name) { $valuesToInsert[$name] = $query->createNamedParameter($values[$name]); } $query->insert('calendarsubscriptions') ->values($valuesToInsert) ->execute(); return $this->db->lastInsertId('*PREFIX*calendarsubscriptions'); } /** * Updates a subscription * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documentation for more info and examples. * * @param mixed $subscriptionId * @param PropPatch $propPatch * @return void */ function updateSubscription($subscriptionId, PropPatch $propPatch) { $supportedProperties = array_keys($this->subscriptionPropertyMap); $supportedProperties[] = '{http://calendarserver.org/ns/}source'; $propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) { $newValues = []; foreach($mutations as $propertyName=>$propertyValue) { if ($propertyName === '{http://calendarserver.org/ns/}source') { $newValues['source'] = $propertyValue->getHref(); } else { $fieldName = $this->subscriptionPropertyMap[$propertyName]; $newValues[$fieldName] = $propertyValue; } } $query = $this->db->getQueryBuilder(); $query->update('calendarsubscriptions') ->set('lastmodified', $query->createNamedParameter(time())); foreach($newValues as $fieldName=>$value) { $query->set($fieldName, $query->createNamedParameter($value)); } $query->where($query->expr()->eq('id', $query->createNamedParameter($subscriptionId))) ->execute(); return true; }); } /** * Deletes a subscription. * * @param mixed $subscriptionId * @return void */ function deleteSubscription($subscriptionId) { $query = $this->db->getQueryBuilder(); $query->delete('calendarsubscriptions') ->where($query->expr()->eq('id', $query->createNamedParameter($subscriptionId))) ->execute(); } /** * Returns a single scheduling object for the inbox collection. * * The returned array should contain the following elements: * * uri - A unique basename for the object. This will be used to * construct a full uri. * * calendardata - The iCalendar object * * lastmodified - The last modification date. Can be an int for a unix * timestamp, or a PHP DateTime object. * * etag - A unique token that must change if the object changed. * * size - The size of the object, in bytes. * * @param string $principalUri * @param string $objectUri * @return array */ function getSchedulingObject($principalUri, $objectUri) { $query = $this->db->getQueryBuilder(); $stmt = $query->select(['uri', 'calendardata', 'lastmodified', 'etag', 'size']) ->from('schedulingobjects') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri))) ->execute(); $row = $stmt->fetch(\PDO::FETCH_ASSOC); if(!$row) { return null; } return [ 'uri' => $row['uri'], 'calendardata' => $row['calendardata'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'size' => (int)$row['size'], ]; } /** * Returns all scheduling objects for the inbox collection. * * These objects should be returned as an array. Every item in the array * should follow the same structure as returned from getSchedulingObject. * * The main difference is that 'calendardata' is optional. * * @param string $principalUri * @return array */ function getSchedulingObjects($principalUri) { $query = $this->db->getQueryBuilder(); $stmt = $query->select(['uri', 'calendardata', 'lastmodified', 'etag', 'size']) ->from('schedulingobjects') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))) ->execute(); $result = []; foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { $result[] = [ 'calendardata' => $row['calendardata'], 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'size' => (int)$row['size'], ]; } return $result; } /** * Deletes a scheduling object from the inbox collection. * * @param string $principalUri * @param string $objectUri * @return void */ function deleteSchedulingObject($principalUri, $objectUri) { $query = $this->db->getQueryBuilder(); $query->delete('schedulingobjects') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri))) ->execute(); } /** * Creates a new scheduling object. This should land in a users' inbox. * * @param string $principalUri * @param string $objectUri * @param string $objectData * @return void */ function createSchedulingObject($principalUri, $objectUri, $objectData) { $query = $this->db->getQueryBuilder(); $query->insert('schedulingobjects') ->values([ 'principaluri' => $query->createNamedParameter($principalUri), 'calendardata' => $query->createNamedParameter($objectData), 'uri' => $query->createNamedParameter($objectUri), 'lastmodified' => $query->createNamedParameter(time()), 'etag' => $query->createNamedParameter(md5($objectData)), 'size' => $query->createNamedParameter(strlen($objectData)) ]) ->execute(); } /** * Adds a change record to the calendarchanges table. * * @param mixed $calendarId * @param string $objectUri * @param int $operation 1 = add, 2 = modify, 3 = delete. * @return void */ protected function addChange($calendarId, $objectUri, $operation) { $stmt = $this->db->prepare('INSERT INTO `*PREFIX*calendarchanges` (`uri`, `synctoken`, `calendarid`, `operation`) SELECT ?, `synctoken`, ?, ? FROM `*PREFIX*calendars` WHERE `id` = ?'); $stmt->execute([ $objectUri, $calendarId, $operation, $calendarId ]); $stmt = $this->db->prepare('UPDATE `*PREFIX*calendars` SET `synctoken` = `synctoken` + 1 WHERE `id` = ?'); $stmt->execute([ $calendarId ]); } /** * Parses some information from calendar objects, used for optimized * calendar-queries. * * Returns an array with the following keys: * * etag - An md5 checksum of the object without the quotes. * * size - Size of the object in bytes * * componentType - VEVENT, VTODO or VJOURNAL * * firstOccurence * * lastOccurence * * uid - value of the UID property * * @param string $calendarData * @return array */ public function getDenormalizedData($calendarData) { $vObject = Reader::read($calendarData); $componentType = null; $component = null; $firstOccurrence = null; $lastOccurrence = null; $uid = null; $classification = self::CLASSIFICATION_PUBLIC; foreach($vObject->getComponents() as $component) { if ($component->name!=='VTIMEZONE') { $componentType = $component->name; $uid = (string)$component->UID; break; } } if (!$componentType) { throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); } if ($componentType === 'VEVENT' && $component->DTSTART) { $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp(); // Finding the last occurrence is a bit harder if (!isset($component->RRULE)) { if (isset($component->DTEND)) { $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp(); } elseif (isset($component->DURATION)) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->add(DateTimeParser::parse($component->DURATION->getValue())); $lastOccurrence = $endDate->getTimeStamp(); } elseif (!$component->DTSTART->hasTime()) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->modify('+1 day'); $lastOccurrence = $endDate->getTimeStamp(); } else { $lastOccurrence = $firstOccurrence; } } else { $it = new EventIterator($vObject, (string)$component->UID); $maxDate = new \DateTime(self::MAX_DATE); if ($it->isInfinite()) { $lastOccurrence = $maxDate->getTimestamp(); } else { $end = $it->getDtEnd(); while($it->valid() && $end < $maxDate) { $end = $it->getDtEnd(); $it->next(); } $lastOccurrence = $end->getTimestamp(); } } } if ($component->CLASS) { $classification = CalDavBackend::CLASSIFICATION_PRIVATE; switch ($component->CLASS->getValue()) { case 'PUBLIC': $classification = CalDavBackend::CLASSIFICATION_PUBLIC; break; case 'CONFIDENTIAL': $classification = CalDavBackend::CLASSIFICATION_CONFIDENTIAL; break; } } return [ 'etag' => md5($calendarData), 'size' => strlen($calendarData), 'componentType' => $componentType, 'firstOccurence' => is_null($firstOccurrence) ? null : max(0, $firstOccurrence), 'lastOccurence' => $lastOccurrence, 'uid' => $uid, 'classification' => $classification ]; } private function readBlob($cardData) { if (is_resource($cardData)) { return stream_get_contents($cardData); } return $cardData; } /** * @param IShareable $shareable * @param array $add * @param array $remove */ public function updateShares($shareable, $add, $remove) { $calendarId = $shareable->getResourceId(); $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateShares', new GenericEvent( '\OCA\DAV\CalDAV\CalDavBackend::updateShares', [ 'calendarId' => $calendarId, 'calendarData' => $this->getCalendarById($calendarId), 'shares' => $this->getShares($calendarId), 'add' => $add, 'remove' => $remove, ])); $this->sharingBackend->updateShares($shareable, $add, $remove); } /** * @param int $resourceId * @return array */ public function getShares($resourceId) { return $this->sharingBackend->getShares($resourceId); } /** * @param boolean $value * @param \OCA\DAV\CalDAV\Calendar $calendar * @return string|null */ public function setPublishStatus($value, $calendar) { $query = $this->db->getQueryBuilder(); if ($value) { $publicUri = $this->random->generate(16, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS); $query->insert('dav_shares') ->values([ 'principaluri' => $query->createNamedParameter($calendar->getPrincipalURI()), 'type' => $query->createNamedParameter('calendar'), 'access' => $query->createNamedParameter(self::ACCESS_PUBLIC), 'resourceid' => $query->createNamedParameter($calendar->getResourceId()), 'publicuri' => $query->createNamedParameter($publicUri) ]); $query->execute(); return $publicUri; } $query->delete('dav_shares') ->where($query->expr()->eq('resourceid', $query->createNamedParameter($calendar->getResourceId()))) ->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC))); $query->execute(); return null; } /** * @param \OCA\DAV\CalDAV\Calendar $calendar * @return mixed */ public function getPublishStatus($calendar) { $query = $this->db->getQueryBuilder(); $result = $query->select('publicuri') ->from('dav_shares') ->where($query->expr()->eq('resourceid', $query->createNamedParameter($calendar->getResourceId()))) ->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC))) ->execute(); $row = $result->fetch(); $result->closeCursor(); return $row ? reset($row) : false; } /** * @param int $resourceId * @param array $acl * @return array */ public function applyShareAcl($resourceId, $acl) { return $this->sharingBackend->applyShareAcl($resourceId, $acl); } /** * update properties table * * @param int $calendarId * @param string $objectUri * @param string $calendarData */ public function updateProperties($calendarId, $objectUri, $calendarData) { $objectId = $this->getCalendarObjectId($calendarId, $objectUri); try { $vCalendar = $this->readCalendarData($calendarData); } catch (\Exception $ex) { return; } $this->purgeProperties($calendarId, $objectId); $query = $this->db->getQueryBuilder(); $query->insert($this->dbObjectPropertiesTable) ->values( [ 'calendarid' => $query->createNamedParameter($calendarId), 'objectid' => $query->createNamedParameter($objectId), 'name' => $query->createParameter('name'), 'parameter' => $query->createParameter('parameter'), 'value' => $query->createParameter('value'), ] ); $indexComponents = ['VEVENT', 'VJOURNAL', 'VTODO']; foreach ($vCalendar->getComponents() as $component) { if (!in_array($component->name, $indexComponents)) { continue; } foreach ($component->children() as $property) { if (in_array($property->name, self::$indexProperties)) { $value = $property->getValue(); // is this a shitty db? if (!$this->db->supports4ByteText()) { $value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value); } $value = substr($value, 0, 254); $query->setParameter('name', $property->name); $query->setParameter('parameter', null); $query->setParameter('value', $value); $query->execute(); } if (in_array($property->name, array_keys(self::$indexParameters))) { $parameters = $property->parameters(); $indexedParametersForProperty = self::$indexParameters[$property->name]; foreach ($parameters as $key => $value) { if (in_array($key, $indexedParametersForProperty)) { // is this a shitty db? if ($this->db->supports4ByteText()) { $value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value); } $value = substr($value, 0, 254); $query->setParameter('name', $property->name); $query->setParameter('parameter', substr($key, 0, 254)); $query->setParameter('value', substr($value, 0, 254)); $query->execute(); } } } } } } /** * read VCalendar data into a VCalendar object * * @param string $objectData * @return VCalendar */ protected function readCalendarData($objectData) { return Reader::read($objectData); } /** * delete all properties from a given calendar object * * @param int $calendarId * @param int $objectId */ protected function purgeProperties($calendarId, $objectId) { $query = $this->db->getQueryBuilder(); $query->delete($this->dbObjectPropertiesTable) ->where($query->expr()->eq('objectid', $query->createNamedParameter($objectId))) ->andWhere($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))); $query->execute(); } /** * get ID from a given calendar object * * @param int $calendarId * @param string $uri * @return int */ protected function getCalendarObjectId($calendarId, $uri) { $query = $this->db->getQueryBuilder(); $query->select('id')->from('calendarobjects') ->where($query->expr()->eq('uri', $query->createNamedParameter($uri))) ->andWhere($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))); $result = $query->execute(); $objectIds = $result->fetch(); $result->closeCursor(); if (!isset($objectIds['id'])) { throw new \InvalidArgumentException('Calendarobject does not exists: ' . $uri); } return (int)$objectIds['id']; } private function convertPrincipal($principalUri, $toV2) { if ($this->principalBackend->getPrincipalPrefix() === 'principals') { list(, $name) = URLUtil::splitPath($principalUri); if ($toV2 === true) { return "principals/users/$name"; } return "principals/$name"; } return $principalUri; } private function addOwnerPrincipal(&$calendarInfo) { $ownerPrincipalKey = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal'; $displaynameKey = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}owner-displayname'; if (isset($calendarInfo[$ownerPrincipalKey])) { $uri = $calendarInfo[$ownerPrincipalKey]; } else { $uri = $calendarInfo['principaluri']; } $principalInformation = $this->principalBackend->getPrincipalByPath($uri); if (isset($principalInformation['{DAV:}displayname'])) { $calendarInfo[$displaynameKey] = $principalInformation['{DAV:}displayname']; } } } CalDAV/Publishing/Xml/Publisher.php 0000604 00000004176 15247164651 0013127 0 ustar 00 <?php /** * @author Thomas Citharel <tcit@tcit.fr> * * @copyright Copyright (c) 2016 Thomas Citharel <tcit@tcit.fr> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Publishing\Xml; use Sabre\Xml\Writer; use Sabre\Xml\XmlSerializable; class Publisher implements XmlSerializable { /** * @var string $publishUrl */ protected $publishUrl; /** * @var boolean $isPublished */ protected $isPublished; /** * @param string $publishUrl * @param boolean $isPublished */ function __construct($publishUrl, $isPublished) { $this->publishUrl = $publishUrl; $this->isPublished = $isPublished; } /** * @return string */ function getValue() { return $this->publishUrl; } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Writer $writer) { if (!$this->isPublished) { // for pre-publish-url $writer->write($this->publishUrl); } else { // for publish-url $writer->writeElement('{DAV:}href', $this->publishUrl); } } } CalDAV/Publishing/PublishPlugin.php 0000604 00000014211 15247164651 0013206 0 ustar 00 <?php /** * @author Thomas Citharel <tcit@tcit.fr> * * @copyright Copyright (c) 2016 Thomas Citharel <tcit@tcit.fr> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Publishing; use Sabre\DAV\PropFind; use Sabre\DAV\INode; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; use Sabre\DAV\Exception\NotFound; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; use Sabre\CalDAV\Xml\Property\AllowedSharingModes; use OCA\DAV\CalDAV\Publishing\Xml\Publisher; use OCA\DAV\CalDAV\Calendar; use OCP\IURLGenerator; use OCP\IConfig; class PublishPlugin extends ServerPlugin { const NS_CALENDARSERVER = 'http://calendarserver.org/ns/'; /** * Reference to SabreDAV server object. * * @var \Sabre\DAV\Server */ protected $server; /** * Config instance to get instance secret. * * @var IConfig */ protected $config; /** * URL Generator for absolute URLs. * * @var IURLGenerator */ protected $urlGenerator; /** * PublishPlugin constructor. * * @param IConfig $config * @param IURLGenerator $urlGenerator */ public function __construct(IConfig $config, IURLGenerator $urlGenerator) { $this->config = $config; $this->urlGenerator = $urlGenerator; } /** * This method should return a list of server-features. * * This is for example 'versioning' and is added to the DAV: header * in an OPTIONS response. * * @return string[] */ public function getFeatures() { // May have to be changed to be detected return ['oc-calendar-publishing', 'calendarserver-sharing']; } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using Sabre\DAV\Server::getPlugin * * @return string */ public function getPluginName() { return 'oc-calendar-publishing'; } /** * This initializes the plugin. * * This function is called by Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param Server $server */ public function initialize(Server $server) { $this->server = $server; $this->server->on('method:POST', [$this, 'httpPost']); $this->server->on('propFind', [$this, 'propFind']); } public function propFind(PropFind $propFind, INode $node) { if ($node instanceof Calendar) { $propFind->handle('{'.self::NS_CALENDARSERVER.'}publish-url', function () use ($node) { if ($node->getPublishStatus()) { // We return the publish-url only if the calendar is published. $token = $node->getPublishStatus(); $publishUrl = $this->urlGenerator->getAbsoluteURL($this->server->getBaseUri().'public-calendars/').$token; return new Publisher($publishUrl, true); } }); $propFind->handle('{'.self::NS_CALENDARSERVER.'}allowed-sharing-modes', function() use ($node) { return new AllowedSharingModes(!$node->isSubscription(), !$node->isSubscription()); }); } } /** * We intercept this to handle POST requests on calendars. * * @param RequestInterface $request * @param ResponseInterface $response * * @return void|bool */ public function httpPost(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Only handling xml $contentType = $request->getHeader('Content-Type'); if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false) { return; } // Making sure the node exists try { $node = $this->server->tree->getNodeForPath($path); } catch (NotFound $e) { return; } $requestBody = $request->getBodyAsString(); // If this request handler could not deal with this POST request, it // will return 'null' and other plugins get a chance to handle the // request. // // However, we already requested the full body. This is a problem, // because a body can only be read once. This is why we preemptively // re-populated the request body with the existing data. $request->setBody($requestBody); $this->server->xml->parse($requestBody, $request->getUrl(), $documentType); switch ($documentType) { case '{'.self::NS_CALENDARSERVER.'}publish-calendar' : // We can only deal with IShareableCalendar objects if (!$node instanceof Calendar) { return; } $this->server->transactionType = 'post-publish-calendar'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $node->setPublishStatus(true); // iCloud sends back the 202, so we will too. $response->setStatus(202); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; case '{'.self::NS_CALENDARSERVER.'}unpublish-calendar' : // We can only deal with IShareableCalendar objects if (!$node instanceof Calendar) { return; } $this->server->transactionType = 'post-unpublish-calendar'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $node->setPublishStatus(false); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; } } } CalDAV/PublicCalendarObject.php 0000604 00000001666 15247164651 0012326 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Georg Ehrke * * @author Georg Ehrke <oc.list@georgehrke.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; class PublicCalendarObject extends CalendarObject { /** * public calendars are always shared * @return bool */ protected function isShared() { return true; } } CalDAV/Search/Xml/Request/CalendarSearchReport.php 0000604 00000013255 15247164651 0015754 0 ustar 00 <?php /** * @author Georg Ehrke <oc.list@georgehrke.com> * * @copyright Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Search\Xml\Request; use Sabre\DAV\Exception\BadRequest; use Sabre\Xml\Reader; use Sabre\Xml\XmlDeserializable; use OCA\DAV\CalDAV\Search\SearchPlugin; /** * CalendarSearchReport request parser. * * This class parses the {urn:ietf:params:xml:ns:caldav}calendar-query * REPORT, as defined in: * * https:// link to standard */ class CalendarSearchReport implements XmlDeserializable { /** * An array with requested properties. * * @var array */ public $properties; /** * List of property/component filters. * * @var array */ public $filters; /** * @var int */ public $limit; /** * @var int */ public $offset; /** * The deserialize method is called during xml parsing. * * This method is called statically, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * You are responsible for advancing the reader to the next element. Not * doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * * @param Reader $reader * @return mixed */ static function xmlDeserialize(Reader $reader) { $elems = $reader->parseInnerTree([ '{http://nextcloud.com/ns}comp-filter' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\CompFilter', '{http://nextcloud.com/ns}prop-filter' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\PropFilter', '{http://nextcloud.com/ns}param-filter' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\ParamFilter', '{http://nextcloud.com/ns}search-term' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\SearchTermFilter', '{http://nextcloud.com/ns}limit' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\LimitFilter', '{http://nextcloud.com/ns}offset' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\OffsetFilter', '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', ]); $newProps = [ 'filters' => [], 'properties' => [], 'limit' => null, 'offset' => null ]; if (!is_array($elems)) { $elems = []; } foreach ($elems as $elem) { switch ($elem['name']) { case '{DAV:}prop': $newProps['properties'] = array_keys($elem['value']); break; case '{' . SearchPlugin::NS_Nextcloud . '}filter': foreach ($elem['value'] as $subElem) { if ($subElem['name'] === '{' . SearchPlugin::NS_Nextcloud . '}comp-filter') { if (!isset($newProps['filters']['comps']) || !is_array($newProps['filters']['comps'])) { $newProps['filters']['comps'] = []; } $newProps['filters']['comps'][] = $subElem['value']; } elseif ($subElem['name'] === '{' . SearchPlugin::NS_Nextcloud . '}prop-filter') { if (!isset($newProps['filters']['props']) || !is_array($newProps['filters']['props'])) { $newProps['filters']['props'] = []; } $newProps['filters']['props'][] = $subElem['value']; } elseif ($subElem['name'] === '{' . SearchPlugin::NS_Nextcloud . '}param-filter') { if (!isset($newProps['filters']['params']) || !is_array($newProps['filters']['params'])) { $newProps['filters']['params'] = []; } $newProps['filters']['params'][] = $subElem['value']; } elseif ($subElem['name'] === '{' . SearchPlugin::NS_Nextcloud . '}search-term') { $newProps['filters']['search-term'] = $subElem['value']; } } break; case '{' . SearchPlugin::NS_Nextcloud . '}limit': $newProps['limit'] = $elem['value']; break; case '{' . SearchPlugin::NS_Nextcloud . '}offset': $newProps['offset'] = $elem['value']; break; } } if (empty($newProps['filters'])) { throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}filter element is required for this request'); } $propsOrParamsDefined = (!empty($newProps['filters']['props']) || !empty($newProps['filters']['params'])); $noCompsDefined = empty($newProps['filters']['comps']); if ($propsOrParamsDefined && $noCompsDefined) { throw new BadRequest('{' . SearchPlugin::NS_Nextcloud . '}prop-filter or {' . SearchPlugin::NS_Nextcloud . '}param-filter given without any {' . SearchPlugin::NS_Nextcloud . '}comp-filter'); } if (!isset($newProps['filters']['search-term'])) { throw new BadRequest('{' . SearchPlugin::NS_Nextcloud . '}search-term is required for this request'); } if (empty($newProps['filters']['props']) && empty($newProps['filters']['params'])) { throw new BadRequest('At least one{' . SearchPlugin::NS_Nextcloud . '}prop-filter or {' . SearchPlugin::NS_Nextcloud . '}param-filter is required for this request'); } $obj = new self(); foreach ($newProps as $key => $value) { $obj->$key = $value; } return $obj; } } CalDAV/Search/Xml/Filter/OffsetFilter.php 0000604 00000002531 15247164651 0014105 0 ustar 00 <?php /** * @author Georg Ehrke <oc.list@georgehrke.com> * * @copyright Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Search\Xml\Filter; use Sabre\DAV\Exception\BadRequest; use Sabre\Xml\Reader; use Sabre\Xml\XmlDeserializable; use OCA\DAV\CalDAV\Search\SearchPlugin; class OffsetFilter implements XmlDeserializable { /** * @param Reader $reader * @throws BadRequest * @return int */ static function xmlDeserialize(Reader $reader) { $value = $reader->parseInnerTree(); if (!is_int($value) && !is_string($value)) { throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}offset has illegal value'); } return intval($value); } } CalDAV/Search/Xml/Filter/PropFilter.php 0000604 00000002642 15247164651 0013602 0 ustar 00 <?php /** * @author Georg Ehrke <oc.list@georgehrke.com> * * @copyright Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Search\Xml\Filter; use Sabre\DAV\Exception\BadRequest; use Sabre\Xml\Reader; use Sabre\Xml\XmlDeserializable; use OCA\DAV\CalDAV\Search\SearchPlugin; class PropFilter implements XmlDeserializable { /** * @param Reader $reader * @throws BadRequest * @return string */ static function xmlDeserialize(Reader $reader) { $att = $reader->parseAttributes(); $componentName = $att['name']; $reader->parseInnerTree(); if (!is_string($componentName)) { throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}prop-filter requires a valid name attribute'); } return $componentName; } } CalDAV/Search/Xml/Filter/SearchTermFilter.php 0000604 00000002512 15247164651 0014713 0 ustar 00 <?php /** * @author Georg Ehrke <oc.list@georgehrke.com> * * @copyright Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Search\Xml\Filter; use Sabre\DAV\Exception\BadRequest; use Sabre\Xml\Reader; use Sabre\Xml\XmlDeserializable; use OCA\DAV\CalDAV\Search\SearchPlugin; class SearchTermFilter implements XmlDeserializable { /** * @param Reader $reader * @throws BadRequest * @return string */ static function xmlDeserialize(Reader $reader) { $value = $reader->parseInnerTree(); if (!is_string($value)) { throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}search-term has illegal value'); } return $value; } } CalDAV/Search/Xml/Filter/CompFilter.php 0000604 00000002642 15247164651 0013560 0 ustar 00 <?php /** * @author Georg Ehrke <oc.list@georgehrke.com> * * @copyright Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Search\Xml\Filter; use Sabre\DAV\Exception\BadRequest; use Sabre\Xml\Reader; use Sabre\Xml\XmlDeserializable; use OCA\DAV\CalDAV\Search\SearchPlugin; class CompFilter implements XmlDeserializable { /** * @param Reader $reader * @throws BadRequest * @return string */ static function xmlDeserialize(Reader $reader) { $att = $reader->parseAttributes(); $componentName = $att['name']; $reader->parseInnerTree(); if (!is_string($componentName)) { throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}comp-filter requires a valid name attribute'); } return $componentName; } } CalDAV/Search/Xml/Filter/LimitFilter.php 0000604 00000002527 15247164651 0013742 0 ustar 00 <?php /** * @author Georg Ehrke <oc.list@georgehrke.com> * * @copyright Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Search\Xml\Filter; use Sabre\DAV\Exception\BadRequest; use Sabre\Xml\Reader; use Sabre\Xml\XmlDeserializable; use OCA\DAV\CalDAV\Search\SearchPlugin; class LimitFilter implements XmlDeserializable { /** * @param Reader $reader * @throws BadRequest * @return int */ static function xmlDeserialize(Reader $reader) { $value = $reader->parseInnerTree(); if (!is_int($value) && !is_string($value)) { throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}limit has illegal value'); } return intval($value); } } CalDAV/Search/Xml/Filter/ParamFilter.php 0000604 00000003213 15247164651 0013715 0 ustar 00 <?php /** * @author Georg Ehrke <oc.list@georgehrke.com> * * @copyright Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Search\Xml\Filter; use Sabre\DAV\Exception\BadRequest; use Sabre\Xml\Reader; use Sabre\Xml\XmlDeserializable; use OCA\DAV\CalDAV\Search\SearchPlugin; class ParamFilter implements XmlDeserializable { /** * @param Reader $reader * @throws BadRequest * @return string */ static function xmlDeserialize(Reader $reader) { $att = $reader->parseAttributes(); $property = $att['property']; $parameter = $att['name']; $reader->parseInnerTree(); if (!is_string($property)) { throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}param-filter requires a valid property attribute'); } if (!is_string($parameter)) { throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}param-filter requires a valid parameter attribute'); } return [ 'property' => $property, 'parameter' => $parameter, ]; } } CalDAV/Search/SearchPlugin.php 0000604 00000010514 15247164651 0012110 0 ustar 00 <?php /** * @author Georg Ehrke <oc.list@georgehrke.com> * * @copyright Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> * @license GNU AGPL version 3 or any later version * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Search; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; use OCA\DAV\CalDAV\CalendarHome; class SearchPlugin extends ServerPlugin { const NS_Nextcloud = 'http://nextcloud.com/ns'; /** * Reference to SabreDAV server object. * * @var \Sabre\DAV\Server */ protected $server; /** * This method should return a list of server-features. * * This is for example 'versioning' and is added to the DAV: header * in an OPTIONS response. * * @return string[] */ public function getFeatures() { // May have to be changed to be detected return ['nc-calendar-search']; } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using Sabre\DAV\Server::getPlugin * * @return string */ public function getPluginName() { return 'nc-calendar-search'; } /** * This initializes the plugin. * * This function is called by Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param Server $server */ public function initialize(Server $server) { $this->server = $server; $server->on('report', [$this, 'report']); $server->xml->elementMap['{' . self::NS_Nextcloud . '}calendar-search'] = 'OCA\\DAV\\CalDAV\\Search\\Xml\\Request\\CalendarSearchReport'; } /** * This functions handles REPORT requests specific to CalDAV * * @param string $reportName * @param mixed $report * @param mixed $path * @return bool */ public function report($reportName, $report, $path) { switch ($reportName) { case '{' . self::NS_Nextcloud . '}calendar-search': $this->server->transactionType = 'report-nc-calendar-search'; $this->calendarSearch($report); return false; } } /** * Returns a list of reports this plugin supports. * * This will be used in the {DAV:}supported-report-set property. * Note that you still need to subscribe to the 'report' event to actually * implement them * * @param string $uri * @return array */ public function getSupportedReportSet($uri) { $node = $this->server->tree->getNodeForPath($uri); $reports = []; if ($node instanceof CalendarHome) { $reports[] = '{' . self::NS_Nextcloud . '}calendar-search'; } return $reports; } /** * This function handles the calendar-query REPORT * * This report is used by clients to request calendar objects based on * complex conditions. * * @param Xml\Request\CalendarSearchReport $report * @return void */ private function calendarSearch($report) { $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); $depth = $this->server->getHTTPDepth(2); // The default result is an empty array $result = []; // If we're dealing with the calendar home, the calendar home itself is // responsible for the calendar-query if ($node instanceof CalendarHome && $depth == 2) { $nodePaths = $node->calendarSearch($report->filters, $report->limit, $report->offset); foreach ($nodePaths as $path) { list($properties) = $this->server->getPropertiesForPath( $this->server->getRequestUri() . '/' . $path, $report->properties); $result[] = $properties; } } $prefer = $this->server->getHTTPPrefer(); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer'); $this->server->httpResponse->setBody( $this->server->generateMultiStatus($result, $prefer['return'] === 'minimal')); } } CalDAV/Schedule/IMipPlugin.php 0000604 00000013520 15247164651 0012070 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2017, Georg Ehrke * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Georg Ehrke <oc.list@georgehrke.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV\Schedule; use OCP\AppFramework\Utility\ITimeFactory; use OCP\ILogger; use OCP\Mail\IMailer; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\DateTimeParser; use Sabre\VObject\ITip; use Sabre\CalDAV\Schedule\IMipPlugin as SabreIMipPlugin; use Sabre\VObject\Recur\EventIterator; /** * iMIP handler. * * This class is responsible for sending out iMIP messages. iMIP is the * email-based transport for iTIP. iTIP deals with scheduling operations for * iCalendar objects. * * If you want to customize the email that gets sent out, you can do so by * extending this class and overriding the sendMessage method. * * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class IMipPlugin extends SabreIMipPlugin { /** @var IMailer */ private $mailer; /** @var ILogger */ private $logger; /** @var ITimeFactory */ private $timeFactory; const MAX_DATE = '2038-01-01'; /** * Creates the email handler. * * @param IMailer $mailer * @param ILogger $logger * @param ITimeFactory $timeFactory */ function __construct(IMailer $mailer, ILogger $logger, ITimeFactory $timeFactory) { parent::__construct(''); $this->mailer = $mailer; $this->logger = $logger; $this->timeFactory = $timeFactory; } /** * Event handler for the 'schedule' event. * * @param ITip\Message $iTipMessage * @return void */ function schedule(ITip\Message $iTipMessage) { // Not sending any emails if the system considers the update // insignificant. if (!$iTipMessage->significantChange) { if (!$iTipMessage->scheduleStatus) { $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; } return; } $summary = $iTipMessage->message->VEVENT->SUMMARY; if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') { return; } if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') { return; } // don't send out mails for events that already took place if ($this->isEventInThePast($iTipMessage->message)) { return; } $sender = substr($iTipMessage->sender, 7); $recipient = substr($iTipMessage->recipient, 7); $senderName = ($iTipMessage->senderName) ? $iTipMessage->senderName : null; $recipientName = ($iTipMessage->recipientName) ? $iTipMessage->recipientName : null; $subject = 'SabreDAV iTIP message'; switch (strtoupper($iTipMessage->method)) { case 'REPLY' : $subject = 'Re: ' . $summary; break; case 'REQUEST' : $subject = $summary; break; case 'CANCEL' : $subject = 'Cancelled: ' . $summary; break; } $contentType = 'text/calendar; charset=UTF-8; method=' . $iTipMessage->method; $message = $this->mailer->createMessage(); $message->setReplyTo([$sender => $senderName]) ->setTo([$recipient => $recipientName]) ->setSubject($subject) ->setBody($iTipMessage->message->serialize(), $contentType); try { $failed = $this->mailer->send($message); if ($failed) { $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]); $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; } $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip'; } catch(\Exception $ex) { $this->logger->logException($ex, ['app' => 'dav']); $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; } } /** * check if event took place in the past already * @param VCalendar $vObject * @return bool */ private function isEventInThePast(VCalendar $vObject) { $component = $vObject->VEVENT; $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp(); // Finding the last occurrence is a bit harder if (!isset($component->RRULE)) { if (isset($component->DTEND)) { $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp(); } elseif (isset($component->DURATION)) { $endDate = clone $component->DTSTART->getDateTime(); // $component->DTEND->getDateTime() returns DateTimeImmutable $endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue())); $lastOccurrence = $endDate->getTimeStamp(); } elseif (!$component->DTSTART->hasTime()) { $endDate = clone $component->DTSTART->getDateTime(); // $component->DTSTART->getDateTime() returns DateTimeImmutable $endDate = $endDate->modify('+1 day'); $lastOccurrence = $endDate->getTimeStamp(); } else { $lastOccurrence = $firstOccurrence; } } else { $it = new EventIterator($vObject, (string)$component->UID); $maxDate = new \DateTime(self::MAX_DATE); if ($it->isInfinite()) { $lastOccurrence = $maxDate->getTimestamp(); } else { $end = $it->getDtEnd(); while($it->valid() && $end < $maxDate) { $end = $it->getDtEnd(); $it->next(); } $lastOccurrence = $end->getTimestamp(); } } $currentTime = $this->timeFactory->getTime(); return $lastOccurrence < $currentTime; } } CalDAV/Schedule/Plugin.php 0000604 00000006053 15247164651 0011314 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * @copyright Copyright (c) 2016, Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Schedule; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalendarHome; use Sabre\DAV\INode; use Sabre\DAV\PropFind; use Sabre\DAV\Server; use Sabre\DAV\Xml\Property\LocalHref; use Sabre\DAVACL\IPrincipal; class Plugin extends \Sabre\CalDAV\Schedule\Plugin { /** * Initializes the plugin * * @param Server $server * @return void */ function initialize(Server $server) { parent::initialize($server); $server->on('propFind', [$this, 'propFindDefaultCalendarUrl'], 90); } /** * Returns a list of addresses that are associated with a principal. * * @param string $principal * @return array */ protected function getAddressesForPrincipal($principal) { $result = parent::getAddressesForPrincipal($principal); if ($result === null) { $result = []; } return $result; } /** * Always use the personal calendar as target for scheduled events * * @param PropFind $propFind * @param INode $node * @return void */ function propFindDefaultCalendarUrl(PropFind $propFind, INode $node) { if ($node instanceof IPrincipal) { $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function() use ($node) { /** @var \OCA\DAV\CalDAV\Plugin $caldavPlugin */ $caldavPlugin = $this->server->getPlugin('caldav'); $principalUrl = $node->getPrincipalUrl(); $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); if (!$calendarHomePath) { return null; } /** @var CalendarHome $calendarHome */ $calendarHome = $this->server->tree->getNodeForPath($calendarHomePath); if (!$calendarHome->childExists(CalDavBackend::PERSONAL_CALENDAR_URI)) { $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, CalDavBackend::PERSONAL_CALENDAR_URI, [ '{DAV:}displayname' => CalDavBackend::PERSONAL_CALENDAR_NAME, ]); } $result = $this->server->getPropertiesForPath($calendarHomePath . '/' . CalDavBackend::PERSONAL_CALENDAR_URI, [], 1); if (empty($result)) { return null; } return new LocalHref($result[0]['href']); }); } } } CalDAV/PublicCalendarRoot.php 0000604 00000002645 15247164651 0012041 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; use Sabre\DAV\Collection; class PublicCalendarRoot extends Collection { /** @var CalDavBackend */ protected $caldavBackend; /** @var \OCP\IL10N */ protected $l10n; function __construct(CalDavBackend $caldavBackend) { $this->caldavBackend = $caldavBackend; $this->l10n = \OC::$server->getL10N('dav'); } /** * @inheritdoc */ function getName() { return 'public-calendars'; } /** * @inheritdoc */ function getChild($name) { $calendar = $this->caldavBackend->getPublicCalendar($name); return new PublicCalendar($this->caldavBackend, $calendar, $this->l10n); } /** * @inheritdoc */ function getChildren() { return []; } } CalDAV/Plugin.php 0000604 00000002202 15247164651 0007550 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2016, ownCloud GmbH. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; use Sabre\HTTP\URLUtil; class Plugin extends \Sabre\CalDAV\Plugin { /** * @inheritdoc */ function getCalendarHomeForPrincipal($principalUrl) { if (strrpos($principalUrl, 'principals/users', -strlen($principalUrl)) !== false) { list(, $principalId) = URLUtil::splitPath($principalUrl); return self::CALENDAR_ROOT .'/' . $principalId; } return; } } CalDAV/PublicCalendar.php 0000604 00000004664 15247164651 0011200 0 ustar 00 <?php /** * @copyright Copyright (c) 2017, Georg Ehrke * * @author Georg Ehrke <oc.list@georgehrke.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; use Sabre\DAV\Exception\NotFound; class PublicCalendar extends Calendar { /** * @param string $name * @throws NotFound * @return PublicCalendarObject */ public function getChild($name) { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); if (!$obj) { throw new NotFound('Calendar object not found'); } if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { throw new NotFound('Calendar object not found'); } $obj['acl'] = $this->getChildACL(); return new PublicCalendarObject($this->caldavBackend, $this->calendarInfo, $obj); } /** * @return PublicCalendarObject[] */ public function getChildren() { $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); $children = []; foreach ($objs as $obj) { if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { continue; } $obj['acl'] = $this->getChildACL(); $children[] = new PublicCalendarObject($this->caldavBackend, $this->calendarInfo, $obj); } return $children; } /** * @param string[] $paths * @return PublicCalendarObject[] */ public function getMultipleChildren(array $paths) { $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths); $children = []; foreach ($objs as $obj) { if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { continue; } $obj['acl'] = $this->getChildACL(); $children[] = new PublicCalendarObject($this->caldavBackend, $this->calendarInfo, $obj); } return $children; } /** * public calendars are always shared * @return bool */ protected function isShared() { return true; } } CalDAV/Activity/Filter/Todo.php 0000604 00000004475 15247164651 0012256 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Filter; use OCP\Activity\IFilter; use OCP\IL10N; use OCP\IURLGenerator; class Todo implements IFilter { /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'calendar_todo'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('Todos'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 40; } /** * @return string Full URL to an icon, empty string when none is given * @since 11.0.0 */ public function getIcon() { return $this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/checkmark.svg')); } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function filterTypes(array $types) { return array_intersect(['calendar_todo'], $types); } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function allowedApps() { return []; } } CalDAV/Activity/Filter/Calendar.php 0000604 00000004517 15247164651 0013057 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Filter; use OCP\Activity\IFilter; use OCP\IL10N; use OCP\IURLGenerator; class Calendar implements IFilter { /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; public function __construct(IL10N $l, IURLGenerator $url) { $this->l = $l; $this->url = $url; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'calendar'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('Calendar'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 40; } /** * @return string Full URL to an icon, empty string when none is given * @since 11.0.0 */ public function getIcon() { return $this->url->getAbsoluteURL($this->url->imagePath('core', 'places/calendar-dark.svg')); } /** * @param string[] $types * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function filterTypes(array $types) { return array_intersect(['calendar', 'calendar_event'], $types); } /** * @return string[] An array of allowed apps from which activities should be displayed * @since 11.0.0 */ public function allowedApps() { return []; } } CalDAV/Activity/Provider/Todo.php 0000604 00000011142 15247164651 0012610 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Provider; use OCP\Activity\IEvent; class Todo extends Event { /** * @param string $language * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parse($language, IEvent $event, IEvent $previousEvent = null) { if ($event->getApp() !== 'dav' || $event->getType() !== 'calendar_todo') { throw new \InvalidArgumentException(); } $this->l = $this->languageFactory->get('dav', $language); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/checkmark.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/checkmark.svg'))); } if ($event->getSubject() === self::SUBJECT_OBJECT_ADD . '_todo') { $subject = $this->l->t('{actor} created todo {todo} in list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_ADD . '_todo_self') { $subject = $this->l->t('You created todo {todo} in list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_DELETE . '_todo') { $subject = $this->l->t('{actor} deleted todo {todo} from list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_DELETE . '_todo_self') { $subject = $this->l->t('You deleted todo {todo} from list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_UPDATE . '_todo') { $subject = $this->l->t('{actor} updated todo {todo} in list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_UPDATE . '_todo_self') { $subject = $this->l->t('You updated todo {todo} in list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_UPDATE . '_todo_completed') { $subject = $this->l->t('{actor} solved todo {todo} in list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_UPDATE . '_todo_completed_self') { $subject = $this->l->t('You solved todo {todo} in list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_UPDATE . '_todo_needs_action') { $subject = $this->l->t('{actor} reopened todo {todo} in list {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_UPDATE . '_todo_needs_action_self') { $subject = $this->l->t('You reopened todo {todo} in list {calendar}'); } else { throw new \InvalidArgumentException(); } $parsedParameters = $this->getParameters($event); $this->setSubjects($event, $subject, $parsedParameters); $event = $this->eventMerger->mergeEvents('todo', $event, $previousEvent); return $event; } /** * @param IEvent $event * @return array */ protected function getParameters(IEvent $event) { $subject = $event->getSubject(); $parameters = $event->getSubjectParameters(); switch ($subject) { case self::SUBJECT_OBJECT_ADD . '_todo': case self::SUBJECT_OBJECT_DELETE . '_todo': case self::SUBJECT_OBJECT_UPDATE . '_todo': case self::SUBJECT_OBJECT_UPDATE . '_todo_completed': case self::SUBJECT_OBJECT_UPDATE . '_todo_needs_action': return [ 'actor' => $this->generateUserParameter($parameters[0]), 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), 'todo' => $this->generateObjectParameter($parameters[2]), ]; case self::SUBJECT_OBJECT_ADD . '_todo_self': case self::SUBJECT_OBJECT_DELETE . '_todo_self': case self::SUBJECT_OBJECT_UPDATE . '_todo_self': case self::SUBJECT_OBJECT_UPDATE . '_todo_completed_self': case self::SUBJECT_OBJECT_UPDATE . '_todo_needs_action_self': return [ 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), 'todo' => $this->generateObjectParameter($parameters[2]), ]; } throw new \InvalidArgumentException(); } } CalDAV/Activity/Provider/Calendar.php 0000604 00000017451 15247164651 0013425 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Provider; use OCP\Activity\IEvent; use OCP\Activity\IEventMerger; use OCP\Activity\IManager; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUserManager; use OCP\L10N\IFactory; class Calendar extends Base { const SUBJECT_ADD = 'calendar_add'; const SUBJECT_UPDATE = 'calendar_update'; const SUBJECT_DELETE = 'calendar_delete'; const SUBJECT_SHARE_USER = 'calendar_user_share'; const SUBJECT_SHARE_GROUP = 'calendar_group_share'; const SUBJECT_UNSHARE_USER = 'calendar_user_unshare'; const SUBJECT_UNSHARE_GROUP = 'calendar_group_unshare'; /** @var IFactory */ protected $languageFactory; /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** @var IManager */ protected $activityManager; /** @var IEventMerger */ protected $eventMerger; /** * @param IFactory $languageFactory * @param IURLGenerator $url * @param IManager $activityManager * @param IUserManager $userManager * @param IEventMerger $eventMerger */ public function __construct(IFactory $languageFactory, IURLGenerator $url, IManager $activityManager, IUserManager $userManager, IEventMerger $eventMerger) { parent::__construct($userManager); $this->languageFactory = $languageFactory; $this->url = $url; $this->activityManager = $activityManager; $this->eventMerger = $eventMerger; } /** * @param string $language * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parse($language, IEvent $event, IEvent $previousEvent = null) { if ($event->getApp() !== 'dav' || $event->getType() !== 'calendar') { throw new \InvalidArgumentException(); } $this->l = $this->languageFactory->get('dav', $language); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/calendar-dark.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/calendar-dark.svg'))); } if ($event->getSubject() === self::SUBJECT_ADD) { $subject = $this->l->t('{actor} created calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_ADD . '_self') { $subject = $this->l->t('You created calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_DELETE) { $subject = $this->l->t('{actor} deleted calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_DELETE . '_self') { $subject = $this->l->t('You deleted calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_UPDATE) { $subject = $this->l->t('{actor} updated calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_UPDATE . '_self') { $subject = $this->l->t('You updated calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_SHARE_USER) { $subject = $this->l->t('{actor} shared calendar {calendar} with you'); } else if ($event->getSubject() === self::SUBJECT_SHARE_USER . '_you') { $subject = $this->l->t('You shared calendar {calendar} with {user}'); } else if ($event->getSubject() === self::SUBJECT_SHARE_USER . '_by') { $subject = $this->l->t('{actor} shared calendar {calendar} with {user}'); } else if ($event->getSubject() === self::SUBJECT_UNSHARE_USER) { $subject = $this->l->t('{actor} unshared calendar {calendar} from you'); } else if ($event->getSubject() === self::SUBJECT_UNSHARE_USER . '_you') { $subject = $this->l->t('You unshared calendar {calendar} from {user}'); } else if ($event->getSubject() === self::SUBJECT_UNSHARE_USER . '_by') { $subject = $this->l->t('{actor} unshared calendar {calendar} from {user}'); } else if ($event->getSubject() === self::SUBJECT_UNSHARE_USER . '_self') { $subject = $this->l->t('{actor} unshared calendar {calendar} from themselves'); } else if ($event->getSubject() === self::SUBJECT_SHARE_GROUP . '_you') { $subject = $this->l->t('You shared calendar {calendar} with group {group}'); } else if ($event->getSubject() === self::SUBJECT_SHARE_GROUP . '_by') { $subject = $this->l->t('{actor} shared calendar {calendar} with group {group}'); } else if ($event->getSubject() === self::SUBJECT_UNSHARE_GROUP . '_you') { $subject = $this->l->t('You unshared calendar {calendar} from group {group}'); } else if ($event->getSubject() === self::SUBJECT_UNSHARE_GROUP . '_by') { $subject = $this->l->t('{actor} unshared calendar {calendar} from group {group}'); } else { throw new \InvalidArgumentException(); } $parsedParameters = $this->getParameters($event); $this->setSubjects($event, $subject, $parsedParameters); $event = $this->eventMerger->mergeEvents('calendar', $event, $previousEvent); if ($event->getChildEvent() === null) { if (isset($parsedParameters['user'])) { // Couldn't group by calendar, maybe we can group by users $event = $this->eventMerger->mergeEvents('user', $event, $previousEvent); } else if (isset($parsedParameters['group'])) { // Couldn't group by calendar, maybe we can group by groups $event = $this->eventMerger->mergeEvents('group', $event, $previousEvent); } } return $event; } /** * @param IEvent $event * @return array */ protected function getParameters(IEvent $event) { $subject = $event->getSubject(); $parameters = $event->getSubjectParameters(); switch ($subject) { case self::SUBJECT_ADD: case self::SUBJECT_ADD . '_self': case self::SUBJECT_DELETE: case self::SUBJECT_DELETE . '_self': case self::SUBJECT_UPDATE: case self::SUBJECT_UPDATE . '_self': case self::SUBJECT_SHARE_USER: case self::SUBJECT_UNSHARE_USER: case self::SUBJECT_UNSHARE_USER . '_self': return [ 'actor' => $this->generateUserParameter($parameters[0]), 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), ]; case self::SUBJECT_SHARE_USER . '_you': case self::SUBJECT_UNSHARE_USER . '_you': return [ 'user' => $this->generateUserParameter($parameters[0]), 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), ]; case self::SUBJECT_SHARE_USER . '_by': case self::SUBJECT_UNSHARE_USER . '_by': return [ 'user' => $this->generateUserParameter($parameters[0]), 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), 'actor' => $this->generateUserParameter($parameters[2]), ]; case self::SUBJECT_SHARE_GROUP . '_you': case self::SUBJECT_UNSHARE_GROUP . '_you': return [ 'group' => $this->generateGroupParameter($parameters[0]), 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), ]; case self::SUBJECT_SHARE_GROUP . '_by': case self::SUBJECT_UNSHARE_GROUP . '_by': return [ 'group' => $this->generateGroupParameter($parameters[0]), 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), 'actor' => $this->generateUserParameter($parameters[2]), ]; } throw new \InvalidArgumentException(); } } CalDAV/Activity/Provider/Base.php 0000604 00000006022 15247164651 0012556 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Provider; use OCP\Activity\IEvent; use OCP\Activity\IProvider; use OCP\IUser; use OCP\IUserManager; abstract class Base implements IProvider { /** @var IUserManager */ protected $userManager; /** @var string[] cached displayNames - key is the UID and value the displayname */ protected $displayNames = []; /** * @param IUserManager $userManager */ public function __construct(IUserManager $userManager) { $this->userManager = $userManager; } /** * @param IEvent $event * @param string $subject * @param array $parameters */ protected function setSubjects(IEvent $event, $subject, array $parameters) { $placeholders = $replacements = []; foreach ($parameters as $placeholder => $parameter) { $placeholders[] = '{' . $placeholder . '}'; $replacements[] = $parameter['name']; } $event->setParsedSubject(str_replace($placeholders, $replacements, $subject)) ->setRichSubject($subject, $parameters); } /** * @param array $eventData * @return array */ protected function generateObjectParameter($eventData) { if (!is_array($eventData) || !isset($eventData['id']) || !isset($eventData['name'])) { throw new \InvalidArgumentException(); }; return [ 'type' => 'calendar-event', 'id' => $eventData['id'], 'name' => $eventData['name'], ]; } /** * @param int $id * @param string $name * @return array */ protected function generateCalendarParameter($id, $name) { return [ 'type' => 'calendar', 'id' => $id, 'name' => $name, ]; } /** * @param string $id * @return array */ protected function generateGroupParameter($id) { return [ 'type' => 'group', 'id' => $id, 'name' => $id, ]; } /** * @param string $uid * @return array */ protected function generateUserParameter($uid) { if (!isset($this->displayNames[$uid])) { $this->displayNames[$uid] = $this->getDisplayName($uid); } return [ 'type' => 'user', 'id' => $uid, 'name' => $this->displayNames[$uid], ]; } /** * @param string $uid * @return string */ protected function getDisplayName($uid) { $user = $this->userManager->get($uid); if ($user instanceof IUser) { return $user->getDisplayName(); } else { return $uid; } } } CalDAV/Activity/Provider/Event.php 0000604 00000011443 15247164651 0012770 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Provider; use OCP\Activity\IEvent; use OCP\Activity\IEventMerger; use OCP\Activity\IManager; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUserManager; use OCP\L10N\IFactory; class Event extends Base { const SUBJECT_OBJECT_ADD = 'object_add'; const SUBJECT_OBJECT_UPDATE = 'object_update'; const SUBJECT_OBJECT_DELETE = 'object_delete'; /** @var IFactory */ protected $languageFactory; /** @var IL10N */ protected $l; /** @var IURLGenerator */ protected $url; /** @var IManager */ protected $activityManager; /** @var IEventMerger */ protected $eventMerger; /** * @param IFactory $languageFactory * @param IURLGenerator $url * @param IManager $activityManager * @param IUserManager $userManager * @param IEventMerger $eventMerger */ public function __construct(IFactory $languageFactory, IURLGenerator $url, IManager $activityManager, IUserManager $userManager, IEventMerger $eventMerger) { parent::__construct($userManager); $this->languageFactory = $languageFactory; $this->url = $url; $this->activityManager = $activityManager; $this->eventMerger = $eventMerger; } /** * @param string $language * @param IEvent $event * @param IEvent|null $previousEvent * @return IEvent * @throws \InvalidArgumentException * @since 11.0.0 */ public function parse($language, IEvent $event, IEvent $previousEvent = null) { if ($event->getApp() !== 'dav' || $event->getType() !== 'calendar_event') { throw new \InvalidArgumentException(); } $this->l = $this->languageFactory->get('dav', $language); if ($this->activityManager->getRequirePNG()) { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/calendar-dark.png'))); } else { $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/calendar-dark.svg'))); } if ($event->getSubject() === self::SUBJECT_OBJECT_ADD . '_event') { $subject = $this->l->t('{actor} created event {event} in calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_ADD . '_event_self') { $subject = $this->l->t('You created event {event} in calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_DELETE . '_event') { $subject = $this->l->t('{actor} deleted event {event} from calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_DELETE . '_event_self') { $subject = $this->l->t('You deleted event {event} from calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_UPDATE . '_event') { $subject = $this->l->t('{actor} updated event {event} in calendar {calendar}'); } else if ($event->getSubject() === self::SUBJECT_OBJECT_UPDATE . '_event_self') { $subject = $this->l->t('You updated event {event} in calendar {calendar}'); } else { throw new \InvalidArgumentException(); } $parsedParameters = $this->getParameters($event); $this->setSubjects($event, $subject, $parsedParameters); $event = $this->eventMerger->mergeEvents('event', $event, $previousEvent); return $event; } /** * @param IEvent $event * @return array */ protected function getParameters(IEvent $event) { $subject = $event->getSubject(); $parameters = $event->getSubjectParameters(); switch ($subject) { case self::SUBJECT_OBJECT_ADD . '_event': case self::SUBJECT_OBJECT_DELETE . '_event': case self::SUBJECT_OBJECT_UPDATE . '_event': return [ 'actor' => $this->generateUserParameter($parameters[0]), 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), 'event' => $this->generateObjectParameter($parameters[2]), ]; case self::SUBJECT_OBJECT_ADD . '_event_self': case self::SUBJECT_OBJECT_DELETE . '_event_self': case self::SUBJECT_OBJECT_UPDATE . '_event_self': return [ 'calendar' => $this->generateCalendarParameter($event->getObjectId(), $parameters[1]), 'event' => $this->generateObjectParameter($parameters[2]), ]; } throw new \InvalidArgumentException(); } } CalDAV/Activity/Setting/Event.php 0000604 00000004411 15247164651 0012610 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Setting; use OCP\Activity\ISetting; use OCP\IL10N; class Event implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'calendar_event'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('A calendar <strong>event</strong> was modified'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 50; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } CalDAV/Activity/Setting/Todo.php 0000604 00000004406 15247164651 0012440 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Setting; use OCP\Activity\ISetting; use OCP\IL10N; class Todo implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'calendar_todo'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('A calendar <strong>todo</strong> was modified'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 50; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } CalDAV/Activity/Setting/Calendar.php 0000604 00000004400 15247164651 0013236 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity\Setting; use OCP\Activity\ISetting; use OCP\IL10N; class Calendar implements ISetting { /** @var IL10N */ protected $l; /** * @param IL10N $l */ public function __construct(IL10N $l) { $this->l = $l; } /** * @return string Lowercase a-z and underscore only identifier * @since 11.0.0 */ public function getIdentifier() { return 'calendar'; } /** * @return string A translated string * @since 11.0.0 */ public function getName() { return $this->l->t('A <strong>calendar</strong> was modified'); } /** * @return int whether the filter should be rather on the top or bottom of * the admin section. The filters are arranged in ascending order of the * priority values. It is required to return a value between 0 and 100. * @since 11.0.0 */ public function getPriority() { return 50; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function canChangeStream() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledStream() { return true; } /** * @return bool True when the option can be changed for the mail * @since 11.0.0 */ public function canChangeMail() { return true; } /** * @return bool True when the option can be changed for the stream * @since 11.0.0 */ public function isDefaultEnabledMail() { return false; } } CalDAV/Activity/Backend.php 0000604 00000032017 15247164651 0011444 0 ustar 00 <?php /** * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\CalDAV\Activity; use OCA\DAV\CalDAV\Activity\Provider\Calendar; use OCA\DAV\CalDAV\Activity\Provider\Event; use OCP\Activity\IEvent; use OCP\Activity\IManager as IActivityManager; use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserSession; use Sabre\VObject\Reader; /** * Class Backend * * @package OCA\DAV\CalDAV\Activity */ class Backend { /** @var IActivityManager */ protected $activityManager; /** @var IGroupManager */ protected $groupManager; /** @var IUserSession */ protected $userSession; /** * @param IActivityManager $activityManager * @param IGroupManager $groupManager * @param IUserSession $userSession */ public function __construct(IActivityManager $activityManager, IGroupManager $groupManager, IUserSession $userSession) { $this->activityManager = $activityManager; $this->groupManager = $groupManager; $this->userSession = $userSession; } /** * Creates activities when a calendar was creates * * @param array $calendarData */ public function onCalendarAdd(array $calendarData) { $this->triggerCalendarActivity(Calendar::SUBJECT_ADD, $calendarData); } /** * Creates activities when a calendar was updated * * @param array $calendarData * @param array $shares * @param array $properties */ public function onCalendarUpdate(array $calendarData, array $shares, array $properties) { $this->triggerCalendarActivity(Calendar::SUBJECT_UPDATE, $calendarData, $shares, $properties); } /** * Creates activities when a calendar was deleted * * @param array $calendarData * @param array $shares */ public function onCalendarDelete(array $calendarData, array $shares) { $this->triggerCalendarActivity(Calendar::SUBJECT_DELETE, $calendarData, $shares); } /** * Creates activities for all related users when a calendar was touched * * @param string $action * @param array $calendarData * @param array $shares * @param array $changedProperties */ protected function triggerCalendarActivity($action, array $calendarData, array $shares = [], array $changedProperties = []) { if (!isset($calendarData['principaluri'])) { return; } $principal = explode('/', $calendarData['principaluri']); $owner = array_pop($principal); $currentUser = $this->userSession->getUser(); if ($currentUser instanceof IUser) { $currentUser = $currentUser->getUID(); } else { $currentUser = $owner; } $event = $this->activityManager->generateEvent(); $event->setApp('dav') ->setObject('calendar', (int) $calendarData['id']) ->setType('calendar') ->setAuthor($currentUser); $changedVisibleInformation = array_intersect([ '{DAV:}displayname', '{http://apple.com/ns/ical/}calendar-color' ], array_keys($changedProperties)); if (empty($shares) || ($action === Calendar::SUBJECT_UPDATE && empty($changedVisibleInformation))) { $users = [$owner]; } else { $users = $this->getUsersForShares($shares); $users[] = $owner; } foreach ($users as $user) { $event->setAffectedUser($user) ->setSubject( $user === $currentUser ? $action . '_self' : $action, [ $currentUser, $calendarData['{DAV:}displayname'], ] ); $this->activityManager->publish($event); } } /** * Creates activities for all related users when a calendar was (un-)shared * * @param array $calendarData * @param array $shares * @param array $add * @param array $remove */ public function onCalendarUpdateShares(array $calendarData, array $shares, array $add, array $remove) { $principal = explode('/', $calendarData['principaluri']); $owner = $principal[2]; $currentUser = $this->userSession->getUser(); if ($currentUser instanceof IUser) { $currentUser = $currentUser->getUID(); } else { $currentUser = $owner; } $event = $this->activityManager->generateEvent(); $event->setApp('dav') ->setObject('calendar', (int) $calendarData['id']) ->setType('calendar') ->setAuthor($currentUser); foreach ($remove as $principal) { // principal:principals/users/test $parts = explode(':', $principal, 2); if ($parts[0] !== 'principal') { continue; } $principal = explode('/', $parts[1]); if ($principal[1] === 'users') { $this->triggerActivityUser( $principal[2], $event, $calendarData, Calendar::SUBJECT_UNSHARE_USER, Calendar::SUBJECT_DELETE . '_self' ); if ($owner !== $principal[2]) { $parameters = [ $principal[2], $calendarData['{DAV:}displayname'], ]; if ($owner === $event->getAuthor()) { $subject = Calendar::SUBJECT_UNSHARE_USER . '_you'; } else if ($principal[2] === $event->getAuthor()) { $subject = Calendar::SUBJECT_UNSHARE_USER . '_self'; } else { $event->setAffectedUser($event->getAuthor()) ->setSubject(Calendar::SUBJECT_UNSHARE_USER . '_you', $parameters); $this->activityManager->publish($event); $subject = Calendar::SUBJECT_UNSHARE_USER . '_by'; $parameters[] = $event->getAuthor(); } $event->setAffectedUser($owner) ->setSubject($subject, $parameters); $this->activityManager->publish($event); } } else if ($principal[1] === 'groups') { $this->triggerActivityGroup($principal[2], $event, $calendarData, Calendar::SUBJECT_UNSHARE_USER); $parameters = [ $principal[2], $calendarData['{DAV:}displayname'], ]; if ($owner === $event->getAuthor()) { $subject = Calendar::SUBJECT_UNSHARE_GROUP . '_you'; } else { $event->setAffectedUser($event->getAuthor()) ->setSubject(Calendar::SUBJECT_UNSHARE_GROUP . '_you', $parameters); $this->activityManager->publish($event); $subject = Calendar::SUBJECT_UNSHARE_GROUP . '_by'; $parameters[] = $event->getAuthor(); } $event->setAffectedUser($owner) ->setSubject($subject, $parameters); $this->activityManager->publish($event); } } foreach ($add as $share) { if ($this->isAlreadyShared($share['href'], $shares)) { continue; } // principal:principals/users/test $parts = explode(':', $share['href'], 2); if ($parts[0] !== 'principal') { continue; } $principal = explode('/', $parts[1]); if ($principal[1] === 'users') { $this->triggerActivityUser($principal[2], $event, $calendarData, Calendar::SUBJECT_SHARE_USER); if ($owner !== $principal[2]) { $parameters = [ $principal[2], $calendarData['{DAV:}displayname'], ]; if ($owner === $event->getAuthor()) { $subject = Calendar::SUBJECT_SHARE_USER . '_you'; } else { $event->setAffectedUser($event->getAuthor()) ->setSubject(Calendar::SUBJECT_SHARE_USER . '_you', $parameters); $this->activityManager->publish($event); $subject = Calendar::SUBJECT_SHARE_USER . '_by'; $parameters[] = $event->getAuthor(); } $event->setAffectedUser($owner) ->setSubject($subject, $parameters); $this->activityManager->publish($event); } } else if ($principal[1] === 'groups') { $this->triggerActivityGroup($principal[2], $event, $calendarData, Calendar::SUBJECT_SHARE_USER); $parameters = [ $principal[2], $calendarData['{DAV:}displayname'], ]; if ($owner === $event->getAuthor()) { $subject = Calendar::SUBJECT_SHARE_GROUP . '_you'; } else { $event->setAffectedUser($event->getAuthor()) ->setSubject(Calendar::SUBJECT_SHARE_GROUP . '_you', $parameters); $this->activityManager->publish($event); $subject = Calendar::SUBJECT_SHARE_GROUP . '_by'; $parameters[] = $event->getAuthor(); } $event->setAffectedUser($owner) ->setSubject($subject, $parameters); $this->activityManager->publish($event); } } } /** * Checks if a calendar is already shared with a principal * * @param string $principal * @param array[] $shares * @return bool */ protected function isAlreadyShared($principal, $shares) { foreach ($shares as $share) { if ($principal === $share['href']) { return true; } } return false; } /** * Creates the given activity for all members of the given group * * @param string $gid * @param IEvent $event * @param array $properties * @param string $subject */ protected function triggerActivityGroup($gid, IEvent $event, array $properties, $subject) { $group = $this->groupManager->get($gid); if ($group instanceof IGroup) { foreach ($group->getUsers() as $user) { // Exclude current user if ($user->getUID() !== $event->getAuthor()) { $this->triggerActivityUser($user->getUID(), $event, $properties, $subject); } } } } /** * Creates the given activity for the given user * * @param string $user * @param IEvent $event * @param array $properties * @param string $subject * @param string $subjectSelf */ protected function triggerActivityUser($user, IEvent $event, array $properties, $subject, $subjectSelf = '') { $event->setAffectedUser($user) ->setSubject( $user === $event->getAuthor() && $subjectSelf ? $subjectSelf : $subject, [ $event->getAuthor(), $properties['{DAV:}displayname'], ] ); $this->activityManager->publish($event); } /** * Creates activities when a calendar object was created/updated/deleted * * @param string $action * @param array $calendarData * @param array $shares * @param array $objectData */ public function onTouchCalendarObject($action, array $calendarData, array $shares, array $objectData) { if (!isset($calendarData['principaluri'])) { return; } $principal = explode('/', $calendarData['principaluri']); $owner = array_pop($principal); $currentUser = $this->userSession->getUser(); if ($currentUser instanceof IUser) { $currentUser = $currentUser->getUID(); } else { $currentUser = $owner; } $object = $this->getObjectNameAndType($objectData); $action = $action . '_' . $object['type']; if ($object['type'] === 'todo' && strpos($action, Event::SUBJECT_OBJECT_UPDATE) === 0 && $object['status'] === 'COMPLETED') { $action .= '_completed'; } else if ($object['type'] === 'todo' && strpos($action, Event::SUBJECT_OBJECT_UPDATE) === 0 && $object['status'] === 'NEEDS-ACTION') { $action .= '_needs_action'; } $event = $this->activityManager->generateEvent(); $event->setApp('dav') ->setObject('calendar', (int) $calendarData['id']) ->setType($object['type'] === 'event' ? 'calendar_event' : 'calendar_todo') ->setAuthor($currentUser); $users = $this->getUsersForShares($shares); $users[] = $owner; foreach ($users as $user) { $event->setAffectedUser($user) ->setSubject( $user === $currentUser ? $action . '_self' : $action, [ $currentUser, $calendarData['{DAV:}displayname'], [ 'id' => $object['id'], 'name' => $object['name'], ], ] ); $this->activityManager->publish($event); } } /** * @param array $objectData * @return string[]|bool */ protected function getObjectNameAndType(array $objectData) { $vObject = Reader::read($objectData['calendardata']); $component = $componentType = null; foreach($vObject->getComponents() as $component) { if (in_array($component->name, ['VEVENT', 'VTODO'])) { $componentType = $component->name; break; } } if (!$componentType) { // Calendar objects must have a VEVENT or VTODO component return false; } if ($componentType === 'VEVENT') { return ['id' => (string) $component->UID, 'name' => (string) $component->SUMMARY, 'type' => 'event']; } return ['id' => (string) $component->UID, 'name' => (string) $component->SUMMARY, 'type' => 'todo', 'status' => (string) $component->STATUS]; } /** * Get all users that have access to a given calendar * * @param array $shares * @return string[] */ protected function getUsersForShares(array $shares) { $users = $groups = []; foreach ($shares as $share) { $prinical = explode('/', $share['{http://owncloud.org/ns}principal']); if ($prinical[1] === 'users') { $users[] = $prinical[2]; } else if ($prinical[1] === 'groups') { $groups[] = $prinical[2]; } } if (!empty($groups)) { foreach ($groups as $gid) { $group = $this->groupManager->get($gid); if ($group instanceof IGroup) { foreach ($group->getUsers() as $user) { $users[] = $user->getUID(); } } } } return array_unique($users); } } CalDAV/CalendarRoot.php 0000604 00000001755 15247164651 0010703 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; class CalendarRoot extends \Sabre\CalDAV\CalendarRoot { function getChildForPrincipal(array $principal) { return new CalendarHome($this->caldavBackend, $principal); } } CalDAV/CalendarHome.php 0000604 00000010005 15247164651 0010634 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CalDAV; use Sabre\CalDAV\Backend\BackendInterface; use Sabre\CalDAV\Backend\NotificationSupport; use Sabre\CalDAV\Backend\SchedulingSupport; use Sabre\CalDAV\Backend\SubscriptionSupport; use Sabre\CalDAV\Schedule\Inbox; use Sabre\CalDAV\Schedule\Outbox; use Sabre\CalDAV\Subscriptions\Subscription; use Sabre\DAV\Exception\NotFound; class CalendarHome extends \Sabre\CalDAV\CalendarHome { /** @var \OCP\IL10N */ private $l10n; public function __construct(BackendInterface $caldavBackend, $principalInfo) { parent::__construct($caldavBackend, $principalInfo); $this->l10n = \OC::$server->getL10N('dav'); } /** * @return BackendInterface */ public function getCalDAVBackend() { return $this->caldavBackend; } /** * @inheritdoc */ function getChildren() { $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); $objects = []; foreach ($calendars as $calendar) { $objects[] = new Calendar($this->caldavBackend, $calendar, $this->l10n); } if ($this->caldavBackend instanceof SchedulingSupport) { $objects[] = new Inbox($this->caldavBackend, $this->principalInfo['uri']); $objects[] = new Outbox($this->principalInfo['uri']); } // We're adding a notifications node, if it's supported by the backend. if ($this->caldavBackend instanceof NotificationSupport) { $objects[] = new \Sabre\CalDAV\Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); } // If the backend supports subscriptions, we'll add those as well, if ($this->caldavBackend instanceof SubscriptionSupport) { foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) { $objects[] = new Subscription($this->caldavBackend, $subscription); } } return $objects; } /** * @inheritdoc */ function getChild($name) { // Special nodes if ($name === 'inbox' && $this->caldavBackend instanceof SchedulingSupport) { return new Inbox($this->caldavBackend, $this->principalInfo['uri']); } if ($name === 'outbox' && $this->caldavBackend instanceof SchedulingSupport) { return new Outbox($this->principalInfo['uri']); } if ($name === 'notifications' && $this->caldavBackend instanceof NotificationSupport) { return new \Sabre\CalDAv\Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); } // Calendars foreach ($this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']) as $calendar) { if ($calendar['uri'] === $name) { return new Calendar($this->caldavBackend, $calendar, $this->l10n); } } if ($this->caldavBackend instanceof SubscriptionSupport) { foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) { if ($subscription['uri'] === $name) { return new Subscription($this->caldavBackend, $subscription); } } } throw new NotFound('Node with name \'' . $name . '\' could not be found'); } /** * @param array $filters * @param integer|null $limit * @param integer|null $offset */ function calendarSearch(array $filters, $limit=null, $offset=null) { $principalUri = $this->principalInfo['uri']; return $this->caldavBackend->calendarSearch($principalUri, $filters, $limit, $offset); } } Comments/EntityCollection.php 0000604 00000011270 15247164651 0012342 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Comments; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; use OCP\ILogger; use OCP\IUserManager; use OCP\IUserSession; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\IProperties; use Sabre\DAV\PropPatch; /** * Class EntityCollection * * this represents a specific holder of comments, identified by an entity type * (class member $name) and an entity id (class member $id). * * @package OCA\DAV\Comments */ class EntityCollection extends RootCollection implements IProperties { const PROPERTY_NAME_READ_MARKER = '{http://owncloud.org/ns}readMarker'; /** @var string */ protected $id; /** @var ILogger */ protected $logger; /** * @param string $id * @param string $name * @param ICommentsManager $commentsManager * @param IUserManager $userManager * @param IUserSession $userSession * @param ILogger $logger */ public function __construct( $id, $name, ICommentsManager $commentsManager, IUserManager $userManager, IUserSession $userSession, ILogger $logger ) { foreach(['id', 'name'] as $property) { $$property = trim($$property); if(empty($$property) || !is_string($$property)) { throw new \InvalidArgumentException('"' . $property . '" parameter must be non-empty string'); } } $this->id = $id; $this->name = $name; $this->commentsManager = $commentsManager; $this->logger = $logger; $this->userManager = $userManager; $this->userSession = $userSession; } /** * returns the ID of this entity * * @return string */ public function getId() { return $this->id; } /** * Returns a specific child node, referenced by its name * * This method must throw Sabre\DAV\Exception\NotFound if the node does not * exist. * * @param string $name * @return \Sabre\DAV\INode * @throws NotFound */ function getChild($name) { try { $comment = $this->commentsManager->get($name); return new CommentNode( $this->commentsManager, $comment, $this->userManager, $this->userSession, $this->logger ); } catch (NotFoundException $e) { throw new NotFound(); } } /** * Returns an array with all the child nodes * * @return \Sabre\DAV\INode[] */ function getChildren() { return $this->findChildren(); } /** * Returns an array of comment nodes. Result can be influenced by offset, * limit and date time parameters. * * @param int $limit * @param int $offset * @param \DateTime|null $datetime * @return CommentNode[] */ function findChildren($limit = 0, $offset = 0, \DateTime $datetime = null) { $comments = $this->commentsManager->getForObject($this->name, $this->id, $limit, $offset, $datetime); $result = []; foreach($comments as $comment) { $result[] = new CommentNode( $this->commentsManager, $comment, $this->userManager, $this->userSession, $this->logger ); } return $result; } /** * Checks if a child-node with the specified name exists * * @param string $name * @return bool */ function childExists($name) { try { $this->commentsManager->get($name); return true; } catch (NotFoundException $e) { return false; } } /** * Sets the read marker to the specified date for the logged in user * * @param \DateTime $value * @return bool */ public function setReadMarker($value) { $dateTime = new \DateTime($value); $user = $this->userSession->getUser(); $this->commentsManager->setReadMark($this->name, $this->id, $dateTime, $user); return true; } /** * @inheritdoc */ function propPatch(PropPatch $propPatch) { $propPatch->handle(self::PROPERTY_NAME_READ_MARKER, [$this, 'setReadMarker']); } /** * @inheritdoc */ function getProperties($properties) { $marker = null; $user = $this->userSession->getUser(); if(!is_null($user)) { $marker = $this->commentsManager->getReadMark($this->name, $this->id, $user); } return [self::PROPERTY_NAME_READ_MARKER => $marker]; } } Comments/EntityTypeCollection.php 0000604 00000006267 15247164651 0013216 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Comments; use OCP\Comments\ICommentsManager; use OCP\ILogger; use OCP\IUserManager; use OCP\IUserSession; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\DAV\Exception\NotFound; /** * Class EntityTypeCollection * * This is collection on the type of things a user can leave comments on, for * example: 'files'. * * Its children are instances of EntityCollection (representing a specific * object, for example the file by id). * * @package OCA\DAV\Comments */ class EntityTypeCollection extends RootCollection { /** @var ILogger */ protected $logger; /** @var IUserManager */ protected $userManager; /** @var \Closure */ protected $childExistsFunction; /** * @param string $name * @param ICommentsManager $commentsManager * @param IUserManager $userManager * @param IUserSession $userSession * @param ILogger $logger * @param \Closure $childExistsFunction */ public function __construct( $name, ICommentsManager $commentsManager, IUserManager $userManager, IUserSession $userSession, ILogger $logger, \Closure $childExistsFunction ) { $name = trim($name); if(empty($name) || !is_string($name)) { throw new \InvalidArgumentException('"name" parameter must be non-empty string'); } $this->name = $name; $this->commentsManager = $commentsManager; $this->logger = $logger; $this->userManager = $userManager; $this->userSession = $userSession; $this->childExistsFunction = $childExistsFunction; } /** * Returns a specific child node, referenced by its name * * This method must throw Sabre\DAV\Exception\NotFound if the node does not * exist. * * @param string $name * @return \Sabre\DAV\INode * @throws NotFound */ function getChild($name) { if(!$this->childExists($name)) { throw new NotFound('Entity does not exist or is not available'); } return new EntityCollection( $name, $this->name, $this->commentsManager, $this->userManager, $this->userSession, $this->logger ); } /** * Returns an array with all the child nodes * * @return \Sabre\DAV\INode[] * @throws MethodNotAllowed */ function getChildren() { throw new MethodNotAllowed('No permission to list folder contents'); } /** * Checks if a child-node with the specified name exists * * @param string $name * @return bool */ function childExists($name) { return call_user_func($this->childExistsFunction, $name); } } Comments/RootCollection.php 0000604 00000012012 15247164651 0012004 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Comments; use OCP\Comments\CommentsEntityEvent; use OCP\Comments\ICommentsManager; use OCP\ILogger; use OCP\IUserManager; use OCP\IUserSession; use Sabre\DAV\Exception\NotAuthenticated; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\ICollection; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class RootCollection implements ICollection { /** @var EntityTypeCollection[]|null */ private $entityTypeCollections; /** @var ICommentsManager */ protected $commentsManager; /** @var string */ protected $name = 'comments'; /** @var ILogger */ protected $logger; /** @var IUserManager */ protected $userManager; /** @var IUserSession */ protected $userSession; /** @var EventDispatcherInterface */ protected $dispatcher; /** * @param ICommentsManager $commentsManager * @param IUserManager $userManager * @param IUserSession $userSession * @param EventDispatcherInterface $dispatcher * @param ILogger $logger */ public function __construct( ICommentsManager $commentsManager, IUserManager $userManager, IUserSession $userSession, EventDispatcherInterface $dispatcher, ILogger $logger) { $this->commentsManager = $commentsManager; $this->logger = $logger; $this->userManager = $userManager; $this->userSession = $userSession; $this->dispatcher = $dispatcher; } /** * initializes the collection. At this point of time, we need the logged in * user. Since it is not the case when the instance is created, we cannot * have this in the constructor. * * @throws NotAuthenticated */ protected function initCollections() { if($this->entityTypeCollections !== null) { return; } $user = $this->userSession->getUser(); if(is_null($user)) { throw new NotAuthenticated(); } $event = new CommentsEntityEvent(CommentsEntityEvent::EVENT_ENTITY); $this->dispatcher->dispatch(CommentsEntityEvent::EVENT_ENTITY, $event); $this->entityTypeCollections = []; foreach ($event->getEntityCollections() as $entity => $entityExistsFunction) { $this->entityTypeCollections[$entity] = new EntityTypeCollection( $entity, $this->commentsManager, $this->userManager, $this->userSession, $this->logger, $entityExistsFunction ); } } /** * Creates a new file in the directory * * @param string $name Name of the file * @param resource|string $data Initial payload * @return null|string * @throws Forbidden */ function createFile($name, $data = null) { throw new Forbidden('Cannot create comments by id'); } /** * Creates a new subdirectory * * @param string $name * @throws Forbidden */ function createDirectory($name) { throw new Forbidden('Permission denied to create collections'); } /** * Returns a specific child node, referenced by its name * * This method must throw Sabre\DAV\Exception\NotFound if the node does not * exist. * * @param string $name * @return \Sabre\DAV\INode * @throws NotFound */ function getChild($name) { $this->initCollections(); if(isset($this->entityTypeCollections[$name])) { return $this->entityTypeCollections[$name]; } throw new NotFound('Entity type "' . $name . '" not found."'); } /** * Returns an array with all the child nodes * * @return \Sabre\DAV\INode[] */ function getChildren() { $this->initCollections(); return $this->entityTypeCollections; } /** * Checks if a child-node with the specified name exists * * @param string $name * @return bool */ function childExists($name) { $this->initCollections(); return isset($this->entityTypeCollections[$name]); } /** * Deleted the current node * * @throws Forbidden */ function delete() { throw new Forbidden('Permission denied to delete this collection'); } /** * Returns the name of the node. * * This is used to generate the url. * * @return string */ function getName() { return $this->name; } /** * Renames the node * * @param string $name The new name * @throws Forbidden */ function setName($name) { throw new Forbidden('Permission denied to rename this collection'); } /** * Returns the last modification time, as a unix timestamp * * @return int */ function getLastModified() { return null; } } Comments/CommentNode.php 0000604 00000021001 15247164651 0011253 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Comments; use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; use OCP\Comments\MessageTooLongException; use OCP\ILogger; use OCP\IUserManager; use OCP\IUserSession; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\DAV\PropPatch; class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties { const NS_OWNCLOUD = 'http://owncloud.org/ns'; const PROPERTY_NAME_UNREAD = '{http://owncloud.org/ns}isUnread'; const PROPERTY_NAME_MESSAGE = '{http://owncloud.org/ns}message'; const PROPERTY_NAME_ACTOR_DISPLAYNAME = '{http://owncloud.org/ns}actorDisplayName'; const PROPERTY_NAME_MENTIONS = '{http://owncloud.org/ns}mentions'; const PROPERTY_NAME_MENTION = '{http://owncloud.org/ns}mention'; const PROPERTY_NAME_MENTION_TYPE = '{http://owncloud.org/ns}mentionType'; const PROPERTY_NAME_MENTION_ID = '{http://owncloud.org/ns}mentionId'; const PROPERTY_NAME_MENTION_DISPLAYNAME = '{http://owncloud.org/ns}mentionDisplayName'; /** @var IComment */ public $comment; /** @var ICommentsManager */ protected $commentsManager; /** @var ILogger */ protected $logger; /** @var array list of properties with key being their name and value their setter */ protected $properties = []; /** @var IUserManager */ protected $userManager; /** @var IUserSession */ protected $userSession; /** * CommentNode constructor. * * @param ICommentsManager $commentsManager * @param IComment $comment * @param IUserManager $userManager * @param IUserSession $userSession * @param ILogger $logger */ public function __construct( ICommentsManager $commentsManager, IComment $comment, IUserManager $userManager, IUserSession $userSession, ILogger $logger ) { $this->commentsManager = $commentsManager; $this->comment = $comment; $this->logger = $logger; $methods = get_class_methods($this->comment); $methods = array_filter($methods, function($name){ return strpos($name, 'get') === 0; }); foreach($methods as $getter) { if($getter === 'getMentions') { continue; // special treatment } $name = '{'.self::NS_OWNCLOUD.'}' . lcfirst(substr($getter, 3)); $this->properties[$name] = $getter; } $this->userManager = $userManager; $this->userSession = $userSession; } /** * returns a list of all possible property names * * @return array */ static public function getPropertyNames() { return [ '{http://owncloud.org/ns}id', '{http://owncloud.org/ns}parentId', '{http://owncloud.org/ns}topmostParentId', '{http://owncloud.org/ns}childrenCount', '{http://owncloud.org/ns}verb', '{http://owncloud.org/ns}actorType', '{http://owncloud.org/ns}actorId', '{http://owncloud.org/ns}creationDateTime', '{http://owncloud.org/ns}latestChildDateTime', '{http://owncloud.org/ns}objectType', '{http://owncloud.org/ns}objectId', // re-used property names are defined as constants self::PROPERTY_NAME_MESSAGE, self::PROPERTY_NAME_ACTOR_DISPLAYNAME, self::PROPERTY_NAME_UNREAD, self::PROPERTY_NAME_MENTIONS, self::PROPERTY_NAME_MENTION, self::PROPERTY_NAME_MENTION_TYPE, self::PROPERTY_NAME_MENTION_ID, self::PROPERTY_NAME_MENTION_DISPLAYNAME, ]; } protected function checkWriteAccessOnComment() { $user = $this->userSession->getUser(); if( $this->comment->getActorType() !== 'users' || is_null($user) || $this->comment->getActorId() !== $user->getUID() ) { throw new Forbidden('Only authors are allowed to edit their comment.'); } } /** * Deleted the current node * * @return void */ function delete() { $this->checkWriteAccessOnComment(); $this->commentsManager->delete($this->comment->getId()); } /** * Returns the name of the node. * * This is used to generate the url. * * @return string */ function getName() { return $this->comment->getId(); } /** * Renames the node * * @param string $name The new name * @throws MethodNotAllowed */ function setName($name) { throw new MethodNotAllowed(); } /** * Returns the last modification time, as a unix timestamp * * @return int */ function getLastModified() { return null; } /** * update the comment's message * * @param $propertyValue * @return bool * @throws BadRequest * @throws \Exception */ public function updateComment($propertyValue) { $this->checkWriteAccessOnComment(); try { $this->comment->setMessage($propertyValue); $this->commentsManager->save($this->comment); return true; } catch (\Exception $e) { $this->logger->logException($e, ['app' => 'dav/comments']); if($e instanceof MessageTooLongException) { $msg = 'Message exceeds allowed character limit of '; throw new BadRequest($msg . IComment::MAX_MESSAGE_LENGTH, 0, $e); } throw $e; } } /** * Updates properties on this node. * * This method received a PropPatch object, which contains all the * information about the update. * * To update specific properties, call the 'handle' method on this object. * Read the PropPatch documentation for more information. * * @param PropPatch $propPatch * @return void */ function propPatch(PropPatch $propPatch) { // other properties than 'message' are read only $propPatch->handle(self::PROPERTY_NAME_MESSAGE, [$this, 'updateComment']); } /** * Returns a list of properties for this nodes. * * The properties list is a list of propertynames the client requested, * encoded in clark-notation {xmlnamespace}tagname * * If the array is empty, it means 'all properties' were requested. * * Note that it's fine to liberally give properties back, instead of * conforming to the list of requested properties. * The Server class will filter out the extra. * * @param array $properties * @return array */ function getProperties($properties) { $properties = array_keys($this->properties); $result = []; foreach($properties as $property) { $getter = $this->properties[$property]; if(method_exists($this->comment, $getter)) { $result[$property] = $this->comment->$getter(); } } if($this->comment->getActorType() === 'users') { $user = $this->userManager->get($this->comment->getActorId()); $displayName = is_null($user) ? null : $user->getDisplayName(); $result[self::PROPERTY_NAME_ACTOR_DISPLAYNAME] = $displayName; } $result[self::PROPERTY_NAME_MENTIONS] = $this->composeMentionsPropertyValue(); $unread = null; $user = $this->userSession->getUser(); if(!is_null($user)) { $readUntil = $this->commentsManager->getReadMark( $this->comment->getObjectType(), $this->comment->getObjectId(), $user ); if(is_null($readUntil)) { $unread = 'true'; } else { $unread = $this->comment->getCreationDateTime() > $readUntil; // re-format for output $unread = $unread ? 'true' : 'false'; } } $result[self::PROPERTY_NAME_UNREAD] = $unread; return $result; } /** * transforms a mentions array as returned from IComment->getMentions to an * array with DAV-compatible structure that can be assigned to the * PROPERTY_NAME_MENTION property. * * @return array */ protected function composeMentionsPropertyValue() { return array_map(function($mention) { try { $displayName = $this->commentsManager->resolveDisplayName($mention['type'], $mention['id']); } catch (\OutOfBoundsException $e) { $this->logger->logException($e); // No displayname, upon client's discretion what to display. $displayName = ''; } return [ self::PROPERTY_NAME_MENTION => [ self::PROPERTY_NAME_MENTION_TYPE => $mention['type'], self::PROPERTY_NAME_MENTION_ID => $mention['id'], self::PROPERTY_NAME_MENTION_DISPLAYNAME => $displayName, ] ]; }, $this->comment->getMentions()); } } Comments/CommentsPlugin.php 0000604 00000016441 15247164651 0012023 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Comments; use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; use OCP\IUserSession; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\ReportNotSupported; use Sabre\DAV\Exception\UnsupportedMediaType; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; use Sabre\DAV\Xml\Element\Response; use Sabre\DAV\Xml\Response\MultiStatus; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; use Sabre\Xml\Writer; /** * Sabre plugin to handle comments: */ class CommentsPlugin extends ServerPlugin { // namespace const NS_OWNCLOUD = 'http://owncloud.org/ns'; const REPORT_NAME = '{http://owncloud.org/ns}filter-comments'; const REPORT_PARAM_LIMIT = '{http://owncloud.org/ns}limit'; const REPORT_PARAM_OFFSET = '{http://owncloud.org/ns}offset'; const REPORT_PARAM_TIMESTAMP = '{http://owncloud.org/ns}datetime'; /** @var ICommentsManager */ protected $commentsManager; /** @var \Sabre\DAV\Server $server */ private $server; /** @var \OCP\IUserSession */ protected $userSession; /** * Comments plugin * * @param ICommentsManager $commentsManager * @param IUserSession $userSession */ public function __construct(ICommentsManager $commentsManager, IUserSession $userSession) { $this->commentsManager = $commentsManager; $this->userSession = $userSession; } /** * This initializes the plugin. * * This function is called by Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param Server $server * @return void */ function initialize(Server $server) { $this->server = $server; if(strpos($this->server->getRequestUri(), 'comments/') !== 0) { return; } $this->server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; $this->server->xml->classMap['DateTime'] = function(Writer $writer, \DateTime $value) { $writer->write(\Sabre\HTTP\toDate($value)); }; $this->server->on('report', [$this, 'onReport']); $this->server->on('method:POST', [$this, 'httpPost']); } /** * POST operation on Comments collections * * @param RequestInterface $request request object * @param ResponseInterface $response response object * @return null|false */ public function httpPost(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof EntityCollection) { return null; } $data = $request->getBodyAsString(); $comment = $this->createComment( $node->getName(), $node->getId(), $data, $request->getHeader('Content-Type') ); // update read marker for the current user/poster to avoid // having their own comments marked as unread $node->setReadMarker(null); $url = rtrim($request->getUrl(), '/') . '/' . urlencode($comment->getId()); $response->setHeader('Content-Location', $url); // created $response->setStatus(201); return false; } /** * Returns a list of reports this plugin supports. * * This will be used in the {DAV:}supported-report-set property. * * @param string $uri * @return array */ public function getSupportedReportSet($uri) { return [self::REPORT_NAME]; } /** * REPORT operations to look for comments * * @param string $reportName * @param array $report * @param string $uri * @return bool * @throws NotFound * @throws ReportNotSupported */ public function onReport($reportName, $report, $uri) { $node = $this->server->tree->getNodeForPath($uri); if(!$node instanceof EntityCollection || $reportName !== self::REPORT_NAME) { throw new ReportNotSupported(); } $args = ['limit' => 0, 'offset' => 0, 'datetime' => null]; $acceptableParameters = [ $this::REPORT_PARAM_LIMIT, $this::REPORT_PARAM_OFFSET, $this::REPORT_PARAM_TIMESTAMP ]; $ns = '{' . $this::NS_OWNCLOUD . '}'; foreach($report as $parameter) { if(!in_array($parameter['name'], $acceptableParameters) || empty($parameter['value'])) { continue; } $args[str_replace($ns, '', $parameter['name'])] = $parameter['value']; } if(!is_null($args['datetime'])) { $args['datetime'] = new \DateTime($args['datetime']); } $results = $node->findChildren($args['limit'], $args['offset'], $args['datetime']); $responses = []; foreach($results as $node) { $nodePath = $this->server->getRequestUri() . '/' . $node->comment->getId(); $resultSet = $this->server->getPropertiesForPath($nodePath, CommentNode::getPropertyNames()); if(isset($resultSet[0]) && isset($resultSet[0][200])) { $responses[] = new Response( $this->server->getBaseUri() . $nodePath, [200 => $resultSet[0][200]], 200 ); } } $xml = $this->server->xml->write( '{DAV:}multistatus', new MultiStatus($responses) ); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setBody($xml); return false; } /** * Creates a new comment * * @param string $objectType e.g. "files" * @param string $objectId e.g. the file id * @param string $data JSON encoded string containing the properties of the tag to create * @param string $contentType content type of the data * @return IComment newly created comment * * @throws BadRequest if a field was missing * @throws UnsupportedMediaType if the content type is not supported */ private function createComment($objectType, $objectId, $data, $contentType = 'application/json') { if (explode(';', $contentType)[0] === 'application/json') { $data = json_decode($data, true); } else { throw new UnsupportedMediaType(); } $actorType = $data['actorType']; $actorId = null; if($actorType === 'users') { $user = $this->userSession->getUser(); if(!is_null($user)) { $actorId = $user->getUID(); } } if(is_null($actorId)) { throw new BadRequest('Invalid actor "' . $actorType .'"'); } try { $comment = $this->commentsManager->create($actorType, $actorId, $objectType, $objectId); $comment->setMessage($data['message']); $comment->setVerb($data['verb']); $this->commentsManager->save($comment); return $comment; } catch (\InvalidArgumentException $e) { throw new BadRequest('Invalid input values', 0, $e); } catch (\OCP\Comments\MessageTooLongException $e) { $msg = 'Message exceeds allowed character limit of '; throw new BadRequest($msg . \OCP\Comments\IComment::MAX_MESSAGE_LENGTH, 0, $e); } } } Connector/Sabre/LockPlugin.php 0000604 00000004714 15247164651 0012327 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCA\DAV\Connector\Sabre\Exception\FileLocked; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\ServerPlugin; use Sabre\HTTP\RequestInterface; class LockPlugin extends ServerPlugin { /** * Reference to main server object * * @var \Sabre\DAV\Server */ private $server; /** * {@inheritdoc} */ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $this->server->on('beforeMethod', [$this, 'getLock'], 50); $this->server->on('afterMethod', [$this, 'releaseLock'], 50); } public function getLock(RequestInterface $request) { // we can't listen on 'beforeMethod:PUT' due to order of operations with setting up the tree // so instead we limit ourselves to the PUT method manually if ($request->getMethod() !== 'PUT' || isset($_SERVER['HTTP_OC_CHUNKED'])) { return; } try { $node = $this->server->tree->getNodeForPath($request->getPath()); } catch (NotFound $e) { return; } if ($node instanceof Node) { try { $node->acquireLock(ILockingProvider::LOCK_SHARED); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } } } public function releaseLock(RequestInterface $request) { if ($request->getMethod() !== 'PUT' || isset($_SERVER['HTTP_OC_CHUNKED'])) { return; } try { $node = $this->server->tree->getNodeForPath($request->getPath()); } catch (NotFound $e) { return; } if ($node instanceof Node) { $node->releaseLock(ILockingProvider::LOCK_SHARED); } } } Connector/Sabre/Auth.php 0000604 00000017520 15247164651 0011160 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Christoph Wurst <christoph@owncloud.com> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Markus Goetz <markus@woboq.com> * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use Exception; use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Authentication\TwoFactorAuth\Manager; use OC\Security\Bruteforce\Throttler; use OC\User\Session; use OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden; use OCP\IRequest; use OCP\ISession; use Sabre\DAV\Auth\Backend\AbstractBasic; use Sabre\DAV\Exception\NotAuthenticated; use Sabre\DAV\Exception\ServiceUnavailable; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; class Auth extends AbstractBasic { const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND'; /** @var ISession */ private $session; /** @var Session */ private $userSession; /** @var IRequest */ private $request; /** @var string */ private $currentUser; /** @var Manager */ private $twoFactorManager; /** @var Throttler */ private $throttler; /** * @param ISession $session * @param Session $userSession * @param IRequest $request * @param Manager $twoFactorManager * @param Throttler $throttler * @param string $principalPrefix */ public function __construct(ISession $session, Session $userSession, IRequest $request, Manager $twoFactorManager, Throttler $throttler, $principalPrefix = 'principals/users/') { $this->session = $session; $this->userSession = $userSession; $this->twoFactorManager = $twoFactorManager; $this->request = $request; $this->throttler = $throttler; $this->principalPrefix = $principalPrefix; // setup realm $defaults = new \OCP\Defaults(); $this->realm = $defaults->getName(); } /** * Whether the user has initially authenticated via DAV * * This is required for WebDAV clients that resent the cookies even when the * account was changed. * * @see https://github.com/owncloud/core/issues/13245 * * @param string $username * @return bool */ public function isDavAuthenticated($username) { return !is_null($this->session->get(self::DAV_AUTHENTICATED)) && $this->session->get(self::DAV_AUTHENTICATED) === $username; } /** * Validates a username and password * * This method should return true or false depending on if login * succeeded. * * @param string $username * @param string $password * @return bool * @throws PasswordLoginForbidden */ protected function validateUserPass($username, $password) { if ($this->userSession->isLoggedIn() && $this->isDavAuthenticated($this->userSession->getUser()->getUID()) ) { \OC_Util::setupFS($this->userSession->getUser()->getUID()); $this->session->close(); return true; } else { \OC_Util::setupFS(); //login hooks may need early access to the filesystem try { if ($this->userSession->logClientIn($username, $password, $this->request, $this->throttler)) { \OC_Util::setupFS($this->userSession->getUser()->getUID()); $this->session->set(self::DAV_AUTHENTICATED, $this->userSession->getUser()->getUID()); $this->session->close(); return true; } else { $this->session->close(); return false; } } catch (PasswordLoginForbiddenException $ex) { $this->session->close(); throw new PasswordLoginForbidden(); } } } /** * @param RequestInterface $request * @param ResponseInterface $response * @return array * @throws NotAuthenticated * @throws ServiceUnavailable */ function check(RequestInterface $request, ResponseInterface $response) { try { $result = $this->auth($request, $response); return $result; } catch (NotAuthenticated $e) { throw $e; } catch (Exception $e) { $class = get_class($e); $msg = $e->getMessage(); \OC::$server->getLogger()->logException($e); throw new ServiceUnavailable("$class: $msg"); } } /** * Checks whether a CSRF check is required on the request * * @return bool */ private function requiresCSRFCheck() { // GET requires no check at all if($this->request->getMethod() === 'GET') { return false; } // Official Nextcloud clients require no checks if($this->request->isUserAgent([ IRequest::USER_AGENT_CLIENT_DESKTOP, IRequest::USER_AGENT_CLIENT_ANDROID, IRequest::USER_AGENT_CLIENT_IOS, ])) { return false; } // If not logged-in no check is required if(!$this->userSession->isLoggedIn()) { return false; } // POST always requires a check if($this->request->getMethod() === 'POST') { return true; } // If logged-in AND DAV authenticated no check is required if($this->userSession->isLoggedIn() && $this->isDavAuthenticated($this->userSession->getUser()->getUID())) { return false; } return true; } /** * @param RequestInterface $request * @param ResponseInterface $response * @return array * @throws NotAuthenticated */ private function auth(RequestInterface $request, ResponseInterface $response) { $forcedLogout = false; if(!$this->request->passesCSRFCheck() && $this->requiresCSRFCheck()) { // In case of a fail with POST we need to recheck the credentials if($this->request->getMethod() === 'POST') { $forcedLogout = true; } else { $response->setStatus(401); throw new \Sabre\DAV\Exception\NotAuthenticated('CSRF check not passed.'); } } if($forcedLogout) { $this->userSession->logout(); } else { if($this->twoFactorManager->needsSecondFactor($this->userSession->getUser())) { throw new \Sabre\DAV\Exception\NotAuthenticated('2FA challenge not passed.'); } if (\OC_User::handleApacheAuth() || //Fix for broken webdav clients ($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED))) || //Well behaved clients that only send the cookie are allowed ($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && $request->getHeader('Authorization') === null) ) { $user = $this->userSession->getUser()->getUID(); \OC_Util::setupFS($user); $this->currentUser = $user; $this->session->close(); return [true, $this->principalPrefix . $user]; } } if (!$this->userSession->isLoggedIn() && in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With')))) { // do not re-authenticate over ajax, use dummy auth name to prevent browser popup $response->addHeader('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"'); $response->setStatus(401); throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls'); } $data = parent::check($request, $response); if($data[0] === true) { $startPos = strrpos($data[1], '/') + 1; $user = $this->userSession->getUser()->getUID(); $data[1] = substr_replace($data[1], $user, $startPos); } return $data; } } Connector/Sabre/Principal.php 0000604 00000013646 15247164651 0012205 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; use Sabre\DAV\Exception; use \Sabre\DAV\PropPatch; use Sabre\DAVACL\PrincipalBackend\BackendInterface; use Sabre\HTTP\URLUtil; class Principal implements BackendInterface { /** @var IUserManager */ private $userManager; /** @var IGroupManager */ private $groupManager; /** @var string */ private $principalPrefix; /** @var bool */ private $hasGroups; /** * @param IUserManager $userManager * @param IGroupManager $groupManager * @param string $principalPrefix */ public function __construct(IUserManager $userManager, IGroupManager $groupManager, $principalPrefix = 'principals/users/') { $this->userManager = $userManager; $this->groupManager = $groupManager; $this->principalPrefix = trim($principalPrefix, '/'); $this->hasGroups = ($principalPrefix === 'principals/users/'); } /** * Returns a list of principals based on a prefix. * * This prefix will often contain something like 'principals'. You are only * expected to return principals that are in this base path. * * You are expected to return at least a 'uri' for every user, you can * return any additional properties if you wish so. Common properties are: * {DAV:}displayname * * @param string $prefixPath * @return string[] */ public function getPrincipalsByPrefix($prefixPath) { $principals = []; if ($prefixPath === $this->principalPrefix) { foreach($this->userManager->search('') as $user) { $principals[] = $this->userToPrincipal($user); } } return $principals; } /** * Returns a specific principal, specified by it's path. * The returned structure should be the exact same as from * getPrincipalsByPrefix. * * @param string $path * @return array */ public function getPrincipalByPath($path) { list($prefix, $name) = URLUtil::splitPath($path); if ($prefix === $this->principalPrefix) { $user = $this->userManager->get($name); if (!is_null($user)) { return $this->userToPrincipal($user); } } return null; } /** * Returns the list of members for a group-principal * * @param string $principal * @return string[] * @throws Exception */ public function getGroupMemberSet($principal) { // TODO: for now the group principal has only one member, the user itself $principal = $this->getPrincipalByPath($principal); if (!$principal) { throw new Exception('Principal not found'); } return [$principal['uri']]; } /** * Returns the list of groups a principal is a member of * * @param string $principal * @param bool $needGroups * @return array * @throws Exception */ public function getGroupMembership($principal, $needGroups = false) { list($prefix, $name) = URLUtil::splitPath($principal); if ($prefix === $this->principalPrefix) { $user = $this->userManager->get($name); if (!$user) { throw new Exception('Principal not found'); } if ($this->hasGroups || $needGroups) { $groups = $this->groupManager->getUserGroups($user); $groups = array_map(function($group) { /** @var IGroup $group */ return 'principals/groups/' . urlencode($group->getGID()); }, $groups); return $groups; } } return []; } /** * Updates the list of group members for a group principal. * * The principals should be passed as a list of uri's. * * @param string $principal * @param string[] $members * @throws Exception */ public function setGroupMemberSet($principal, array $members) { throw new Exception('Setting members of the group is not supported yet'); } /** * @param string $path * @param PropPatch $propPatch * @return int */ function updatePrincipal($path, PropPatch $propPatch) { return 0; } /** * @param string $prefixPath * @param array $searchProperties * @param string $test * @return array */ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { return []; } /** * @param string $uri * @param string $principalPrefix * @return string */ function findByUri($uri, $principalPrefix) { if (substr($uri, 0, 7) === 'mailto:') { $email = substr($uri, 7); $users = $this->userManager->getByEmail($email); if (count($users) === 1) { return $this->principalPrefix . '/' . $users[0]->getUID(); } } return ''; } /** * @param IUser $user * @return array */ protected function userToPrincipal($user) { $userId = $user->getUID(); $displayName = $user->getDisplayName(); $principal = [ 'uri' => $this->principalPrefix . '/' . $userId, '{DAV:}displayname' => is_null($displayName) ? $userId : $displayName, ]; $email = $user->getEMailAddress(); if (!empty($email)) { $principal['{http://sabredav.org/ns}email-address'] = $email; } return $principal; } public function getPrincipalPrefix() { return $this->principalPrefix; } } Connector/Sabre/FilesPlugin.php 0000604 00000036727 15247164651 0012512 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OC\Files\View; use OCP\Files\ForbiddenException; use OCP\IPreview; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\IFile; use \Sabre\DAV\PropFind; use \Sabre\DAV\PropPatch; use Sabre\DAV\ServerPlugin; use Sabre\DAV\Tree; use \Sabre\HTTP\RequestInterface; use \Sabre\HTTP\ResponseInterface; use OCP\Files\StorageNotAvailableException; use OCP\IConfig; use OCP\IRequest; use OCA\DAV\Upload\FutureFile; class FilesPlugin extends ServerPlugin { // namespace const NS_OWNCLOUD = 'http://owncloud.org/ns'; const NS_NEXTCLOUD = 'http://nextcloud.org/ns'; const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id'; const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid'; const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions'; const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions'; const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL'; const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size'; const GETETAG_PROPERTYNAME = '{DAV:}getetag'; const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified'; const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id'; const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name'; const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums'; const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint'; const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview'; const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type'; const IS_ENCRYPTED_PROPERTYNAME = '{http://nextcloud.org/ns}is-encrypted'; /** * Reference to main server object * * @var \Sabre\DAV\Server */ private $server; /** * @var Tree */ private $tree; /** * Whether this is public webdav. * If true, some returned information will be stripped off. * * @var bool */ private $isPublic; /** * @var View */ private $fileView; /** * @var bool */ private $downloadAttachment; /** * @var IConfig */ private $config; /** * @var IRequest */ private $request; /** * @var IPreview */ private $previewManager; /** * @param Tree $tree * @param IConfig $config * @param IRequest $request * @param IPreview $previewManager * @param bool $isPublic * @param bool $downloadAttachment */ public function __construct(Tree $tree, IConfig $config, IRequest $request, IPreview $previewManager, $isPublic = false, $downloadAttachment = true) { $this->tree = $tree; $this->config = $config; $this->request = $request; $this->isPublic = $isPublic; $this->downloadAttachment = $downloadAttachment; $this->previewManager = $previewManager; } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; $server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc'; $server->protectedProperties[] = self::FILEID_PROPERTYNAME; $server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME; $server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME; $server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME; $server->protectedProperties[] = self::SIZE_PROPERTYNAME; $server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME; $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME; $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME; $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME; $server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME; $server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME; $server->protectedProperties[] = self::MOUNT_TYPE_PROPERTYNAME; $server->protectedProperties[] = self::IS_ENCRYPTED_PROPERTYNAME; // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH $allowedProperties = ['{DAV:}getetag']; $server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties); $this->server = $server; $this->server->on('propFind', array($this, 'handleGetProperties')); $this->server->on('propPatch', array($this, 'handleUpdateProperties')); $this->server->on('afterBind', array($this, 'sendFileIdHeader')); $this->server->on('afterWriteContent', array($this, 'sendFileIdHeader')); $this->server->on('afterMethod:GET', [$this,'httpGet']); $this->server->on('afterMethod:GET', array($this, 'handleDownloadToken')); $this->server->on('afterResponse', function($request, ResponseInterface $response) { $body = $response->getBody(); if (is_resource($body)) { fclose($body); } }); $this->server->on('beforeMove', [$this, 'checkMove']); $this->server->on('beforeMove', [$this, 'beforeMoveFutureFile']); } /** * Plugin that checks if a move can actually be performed. * * @param string $source source path * @param string $destination destination path * @throws Forbidden * @throws NotFound */ function checkMove($source, $destination) { $sourceNode = $this->tree->getNodeForPath($source); if (!$sourceNode instanceof Node) { return; } list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($source); list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination); if ($sourceDir !== $destinationDir) { $sourceNodeFileInfo = $sourceNode->getFileInfo(); if (is_null($sourceNodeFileInfo)) { throw new NotFound($source . ' does not exist'); } if (!$sourceNodeFileInfo->isDeletable()) { throw new Forbidden($source . " cannot be deleted"); } } } /** * This sets a cookie to be able to recognize the start of the download * the content must not be longer than 32 characters and must only contain * alphanumeric characters * * @param RequestInterface $request * @param ResponseInterface $response */ function handleDownloadToken(RequestInterface $request, ResponseInterface $response) { $queryParams = $request->getQueryParameters(); /** * this sets a cookie to be able to recognize the start of the download * the content must not be longer than 32 characters and must only contain * alphanumeric characters */ if (isset($queryParams['downloadStartSecret'])) { $token = $queryParams['downloadStartSecret']; if (!isset($token[32]) && preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) { // FIXME: use $response->setHeader() instead setcookie('ocDownloadStarted', $token, time() + 20, '/'); } } } /** * Add headers to file download * * @param RequestInterface $request * @param ResponseInterface $response */ function httpGet(RequestInterface $request, ResponseInterface $response) { // Only handle valid files $node = $this->tree->getNodeForPath($request->getPath()); if (!($node instanceof IFile)) return; // adds a 'Content-Disposition: attachment' header in case no disposition // header has been set before if ($this->downloadAttachment && $response->getHeader('Content-Disposition') === null) { $filename = $node->getName(); if ($this->request->isUserAgent( [ \OC\AppFramework\Http\Request::USER_AGENT_IE, \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME, \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX, ])) { $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"'); } else { $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename) . '; filename="' . rawurlencode($filename) . '"'); } } if ($node instanceof \OCA\DAV\Connector\Sabre\File) { //Add OC-Checksum header /** @var $node File */ $checksum = $node->getChecksum(); if ($checksum !== null && $checksum !== '') { $response->addHeader('OC-Checksum', $checksum); } } } /** * Adds all ownCloud-specific properties * * @param PropFind $propFind * @param \Sabre\DAV\INode $node * @return void */ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { $httpRequest = $this->server->httpRequest; if ($node instanceof \OCA\DAV\Connector\Sabre\Node) { /** * This was disabled, because it made dir listing throw an exception, * so users were unable to navigate into folders where one subitem * is blocked by the files_accesscontrol app, see: * https://github.com/nextcloud/files_accesscontrol/issues/65 if (!$node->getFileInfo()->isReadable()) { // avoid detecting files through this means throw new NotFound(); } */ $propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) { return $node->getFileId(); }); $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) { return $node->getInternalFileId(); }); $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) { $perms = $node->getDavPermissions(); if ($this->isPublic) { // remove mount information $perms = str_replace(['S', 'M'], '', $perms); } return $perms; }); $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) { return $node->getSharePermissions( $httpRequest->getRawServerValue('PHP_AUTH_USER') ); }); $propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) { return $node->getETag(); }); $propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) { $owner = $node->getOwner(); if (!$owner) { return null; } else { return $owner->getUID(); } }); $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) { $owner = $node->getOwner(); if (!$owner) { return null; } else { return $owner->getDisplayName(); } }); $propFind->handle(self::IS_ENCRYPTED_PROPERTYNAME, function() use ($node) { $result = $node->getFileInfo()->isEncrypted() ? '1' : '0'; return $result; }); $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) { return json_encode($this->previewManager->isAvailable($node->getFileInfo())); }); $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) { return $node->getSize(); }); $propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) { return $node->getFileInfo()->getMountPoint()->getMountType(); }); } if ($node instanceof \OCA\DAV\Connector\Sabre\Node) { $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) { return $this->config->getSystemValue('data-fingerprint', ''); }); } if ($node instanceof \OCA\DAV\Connector\Sabre\File) { $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) { /** @var $node \OCA\DAV\Connector\Sabre\File */ try { $directDownloadUrl = $node->getDirectDownload(); if (isset($directDownloadUrl['url'])) { return $directDownloadUrl['url']; } } catch (StorageNotAvailableException $e) { return false; } catch (ForbiddenException $e) { return false; } return false; }); $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) { $checksum = $node->getChecksum(); if ($checksum === NULL || $checksum === '') { return null; } return new ChecksumList($checksum); }); } if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) { $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) { return $node->getSize(); }); } } /** * Update ownCloud-specific properties * * @param string $path * @param PropPatch $propPatch * * @return void */ public function handleUpdateProperties($path, PropPatch $propPatch) { $node = $this->tree->getNodeForPath($path); if (!($node instanceof \OCA\DAV\Connector\Sabre\Node)) { return; } $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($node) { if (empty($time)) { return false; } $node->touch($time); return true; }); $propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($node) { if (empty($etag)) { return false; } if ($node->setEtag($etag) !== -1) { return true; } return false; }); } /** * @param string $filePath * @param \Sabre\DAV\INode $node * @throws \Sabre\DAV\Exception\BadRequest */ public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) { // chunked upload handling if (isset($_SERVER['HTTP_OC_CHUNKED'])) { list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($filePath); $info = \OC_FileChunking::decodeName($name); if (!empty($info)) { $filePath = $path . '/' . $info['name']; } } // we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder if (!$this->server->tree->nodeExists($filePath)) { return; } $node = $this->server->tree->getNodeForPath($filePath); if ($node instanceof \OCA\DAV\Connector\Sabre\Node) { $fileId = $node->getFileId(); if (!is_null($fileId)) { $this->server->httpResponse->setHeader('OC-FileId', $fileId); } } } /** * Move handler for future file. * * This overrides the default move behavior to prevent Sabre * to delete the target file before moving. Because deleting would * lose the file id and metadata. * * @param string $path source path * @param string $destination destination path * @return bool|void false to stop handling, void to skip this handler */ public function beforeMoveFutureFile($path, $destination) { $sourceNode = $this->tree->getNodeForPath($path); if (!$sourceNode instanceof FutureFile) { // skip handling as the source is not a chunked FutureFile return; } if (!$this->tree->nodeExists($destination)) { // skip and let the default handler do its work return; } // do a move manually, skipping Sabre's default "delete" for existing nodes $this->tree->move($path, $destination); // trigger all default events (copied from CorePlugin::move) $this->server->emit('afterMove', [$path, $destination]); $this->server->emit('afterUnbind', [$path]); $this->server->emit('afterBind', [$destination]); $response = $this->server->httpResponse; $response->setHeader('Content-Length', '0'); $response->setStatus(204); return false; } } Connector/Sabre/Server.php 0000604 00000002457 15247164651 0011530 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author scolebrook <scolebrook@mac.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; /** * Class \OCA\DAV\Connector\Sabre\Server * * This class overrides some methods from @see \Sabre\DAV\Server. * * @see \Sabre\DAV\Server */ class Server extends \Sabre\DAV\Server { /** * @see \Sabre\DAV\Server */ public function __construct($treeOrNode = null) { parent::__construct($treeOrNode); self::$exposeVersion = false; $this->enablePropfindDepthInfinity = true; } } Connector/Sabre/ChecksumList.php 0000604 00000003754 15247164651 0012661 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use Sabre\Xml\XmlSerializable; use Sabre\Xml\Writer; /** * Checksumlist property * * This property contains multiple "checksum" elements, each containing a * checksum name. */ class ChecksumList implements XmlSerializable { const NS_OWNCLOUD = 'http://owncloud.org/ns'; /** @var string[] of TYPE:CHECKSUM */ private $checksums; /** * @param string $checksum */ public function __construct($checksum) { $this->checksums = explode(',', $checksum); } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Writer $writer) { foreach ($this->checksums as $checksum) { $writer->writeElement('{' . self::NS_OWNCLOUD . '}checksum', $checksum); } } } Connector/Sabre/TagsPlugin.php 0000604 00000016662 15247164651 0012342 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; /** * ownCloud * * @author Vincent Petry * @copyright 2014 Vincent Petry <pvince81@owncloud.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ use \Sabre\DAV\PropFind; use \Sabre\DAV\PropPatch; class TagsPlugin extends \Sabre\DAV\ServerPlugin { // namespace const NS_OWNCLOUD = 'http://owncloud.org/ns'; const TAGS_PROPERTYNAME = '{http://owncloud.org/ns}tags'; const FAVORITE_PROPERTYNAME = '{http://owncloud.org/ns}favorite'; const TAG_FAVORITE = '_$!<Favorite>!$_'; /** * Reference to main server object * * @var \Sabre\DAV\Server */ private $server; /** * @var \OCP\ITagManager */ private $tagManager; /** * @var \OCP\ITags */ private $tagger; /** * Array of file id to tags array * The null value means the cache wasn't initialized. * * @var array */ private $cachedTags; /** * @var \Sabre\DAV\Tree */ private $tree; /** * @param \Sabre\DAV\Tree $tree tree * @param \OCP\ITagManager $tagManager tag manager */ public function __construct(\Sabre\DAV\Tree $tree, \OCP\ITagManager $tagManager) { $this->tree = $tree; $this->tagManager = $tagManager; $this->tagger = null; $this->cachedTags = array(); } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; $server->xml->elementMap[self::TAGS_PROPERTYNAME] = 'OCA\\DAV\\Connector\\Sabre\\TagList'; $this->server = $server; $this->server->on('propFind', array($this, 'handleGetProperties')); $this->server->on('propPatch', array($this, 'handleUpdateProperties')); } /** * Returns the tagger * * @return \OCP\ITags tagger */ private function getTagger() { if (!$this->tagger) { $this->tagger = $this->tagManager->load('files'); } return $this->tagger; } /** * Returns tags and favorites. * * @param integer $fileId file id * @return array list($tags, $favorite) with $tags as tag array * and $favorite is a boolean whether the file was favorited */ private function getTagsAndFav($fileId) { $isFav = false; $tags = $this->getTags($fileId); if ($tags) { $favPos = array_search(self::TAG_FAVORITE, $tags); if ($favPos !== false) { $isFav = true; unset($tags[$favPos]); } } return array($tags, $isFav); } /** * Returns tags for the given file id * * @param integer $fileId file id * @return array list of tags for that file */ private function getTags($fileId) { if (isset($this->cachedTags[$fileId])) { return $this->cachedTags[$fileId]; } else { $tags = $this->getTagger()->getTagsForObjects(array($fileId)); if ($tags !== false) { if (empty($tags)) { return array(); } return current($tags); } } return null; } /** * Updates the tags of the given file id * * @param int $fileId * @param array $tags array of tag strings */ private function updateTags($fileId, $tags) { $tagger = $this->getTagger(); $currentTags = $this->getTags($fileId); $newTags = array_diff($tags, $currentTags); foreach ($newTags as $tag) { if ($tag === self::TAG_FAVORITE) { continue; } $tagger->tagAs($fileId, $tag); } $deletedTags = array_diff($currentTags, $tags); foreach ($deletedTags as $tag) { if ($tag === self::TAG_FAVORITE) { continue; } $tagger->unTag($fileId, $tag); } } /** * Adds tags and favorites properties to the response, * if requested. * * @param PropFind $propFind * @param \Sabre\DAV\INode $node * @return void */ public function handleGetProperties( PropFind $propFind, \Sabre\DAV\INode $node ) { if (!($node instanceof \OCA\DAV\Connector\Sabre\Node)) { return; } // need prefetch ? if ($node instanceof \OCA\DAV\Connector\Sabre\Directory && $propFind->getDepth() !== 0 && (!is_null($propFind->getStatus(self::TAGS_PROPERTYNAME)) || !is_null($propFind->getStatus(self::FAVORITE_PROPERTYNAME)) )) { // note: pre-fetching only supported for depth <= 1 $folderContent = $node->getChildren(); $fileIds[] = (int)$node->getId(); foreach ($folderContent as $info) { $fileIds[] = (int)$info->getId(); } $tags = $this->getTagger()->getTagsForObjects($fileIds); if ($tags === false) { // the tags API returns false on error... $tags = array(); } $this->cachedTags = $this->cachedTags + $tags; $emptyFileIds = array_diff($fileIds, array_keys($tags)); // also cache the ones that were not found foreach ($emptyFileIds as $fileId) { $this->cachedTags[$fileId] = []; } } $tags = null; $isFav = null; $propFind->handle(self::TAGS_PROPERTYNAME, function() use ($tags, &$isFav, $node) { list($tags, $isFav) = $this->getTagsAndFav($node->getId()); return new TagList($tags); }); $propFind->handle(self::FAVORITE_PROPERTYNAME, function() use ($isFav, $node) { if (is_null($isFav)) { list(, $isFav) = $this->getTagsAndFav($node->getId()); } if ($isFav) { return 1; } else { return 0; } }); } /** * Updates tags and favorites properties, if applicable. * * @param string $path * @param PropPatch $propPatch * * @return void */ public function handleUpdateProperties($path, PropPatch $propPatch) { $node = $this->tree->getNodeForPath($path); if (!($node instanceof \OCA\DAV\Connector\Sabre\Node)) { return; } $propPatch->handle(self::TAGS_PROPERTYNAME, function($tagList) use ($node) { $this->updateTags($node->getId(), $tagList->getTags()); return true; }); $propPatch->handle(self::FAVORITE_PROPERTYNAME, function($favState) use ($node) { if ((int)$favState === 1 || $favState === 'true') { $this->getTagger()->tagAs($node->getId(), self::TAG_FAVORITE); } else { $this->getTagger()->unTag($node->getId(), self::TAG_FAVORITE); } if (is_null($favState)) { // confirm deletion return 204; } return 200; }); } } Connector/Sabre/QuotaPlugin.php 0000604 00000010427 15247164651 0012526 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Felix Moeller <mail@felixmoeller.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author scambra <sergio@entrecables.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCP\Files\FileInfo; use OCP\Files\StorageNotAvailableException; use Sabre\DAV\Exception\InsufficientStorage; use Sabre\DAV\Exception\ServiceUnavailable; use Sabre\HTTP\URLUtil; /** * This plugin check user quota and deny creating files when they exceeds the quota. * * @author Sergio Cambra * @copyright Copyright (C) 2012 entreCables S.L. All rights reserved. * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class QuotaPlugin extends \Sabre\DAV\ServerPlugin { /** * @var \OC\Files\View */ private $view; /** * Reference to main server object * * @var \Sabre\DAV\Server */ private $server; /** * @param \OC\Files\View $view */ public function __construct($view) { $this->view = $view; } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the requires event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $server->on('beforeWriteContent', array($this, 'checkQuota'), 10); $server->on('beforeCreateFile', array($this, 'checkQuota'), 10); } /** * This method is called before any HTTP method and validates there is enough free space to store the file * * @param string $uri * @throws InsufficientStorage * @return bool */ public function checkQuota($uri) { $length = $this->getLength(); if ($length) { if (substr($uri, 0, 1) !== '/') { $uri = '/' . $uri; } list($parentUri, $newName) = URLUtil::splitPath($uri); if(is_null($parentUri)) { $parentUri = ''; } $req = $this->server->httpRequest; if ($req->getHeader('OC-Chunked')) { $info = \OC_FileChunking::decodeName($newName); $chunkHandler = $this->getFileChunking($info); // subtract the already uploaded size to see whether // there is still enough space for the remaining chunks $length -= $chunkHandler->getCurrentSize(); // use target file name for free space check in case of shared files $uri = rtrim($parentUri, '/') . '/' . $info['name']; } $freeSpace = $this->getFreeSpace($uri); if ($freeSpace !== FileInfo::SPACE_UNKNOWN && $freeSpace !== FileInfo::SPACE_UNLIMITED && $length > $freeSpace) { if (isset($chunkHandler)) { $chunkHandler->cleanup(); } throw new InsufficientStorage(); } } return true; } public function getFileChunking($info) { // FIXME: need a factory for better mocking support return new \OC_FileChunking($info); } public function getLength() { $req = $this->server->httpRequest; $length = $req->getHeader('X-Expected-Entity-Length'); if (!is_numeric($length)) { $length = $req->getHeader('Content-Length'); $length = is_numeric($length) ? $length : null; } $ocLength = $req->getHeader('OC-Total-Length'); if (is_numeric($length) && is_numeric($ocLength)) { return max($length, $ocLength); } return $length; } /** * @param string $uri * @return mixed * @throws ServiceUnavailable */ public function getFreeSpace($uri) { try { $freeSpace = $this->view->free_space(ltrim($uri, '/')); return $freeSpace; } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable($e->getMessage()); } } } Connector/Sabre/ExceptionLoggerPlugin.php 0000604 00000006054 15247164651 0014534 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Pierre Jochem <pierrejochem@msn.com> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCP\ILogger; use Sabre\DAV\Exception; use Sabre\HTTP\Response; class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { protected $nonFatalExceptions = [ 'Sabre\DAV\Exception\NotAuthenticated' => true, // If tokenauth can throw this exception (which is basically as // NotAuthenticated. So not fatal. 'OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden' => true, // the sync client uses this to find out whether files exist, // so it is not always an error, log it as debug 'Sabre\DAV\Exception\NotFound' => true, // this one mostly happens when the same file is uploaded at // exactly the same time from two clients, only one client // wins, the second one gets "Precondition failed" 'Sabre\DAV\Exception\PreconditionFailed' => true, // forbidden can be expected when trying to upload to // read-only folders for example 'Sabre\DAV\Exception\Forbidden' => true, // Happens when an external storage or federated share is temporarily // not available 'Sabre\DAV\Exception\StorageNotAvailableException' => true, ]; /** @var string */ private $appName; /** @var ILogger */ private $logger; /** * @param string $loggerAppName app name to use when logging * @param ILogger $logger */ public function __construct($loggerAppName, $logger) { $this->appName = $loggerAppName; $this->logger = $logger; } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $server->on('exception', array($this, 'logException'), 10); } /** * Log exception * */ public function logException(\Exception $ex) { $exceptionClass = get_class($ex); $level = \OCP\Util::FATAL; if (isset($this->nonFatalExceptions[$exceptionClass])) { $level = \OCP\Util::DEBUG; } $this->logger->logException($ex, [ 'app' => $this->appName, 'level' => $level, ]); } } Connector/Sabre/ShareTypeList.php 0000604 00000004123 15247164651 0013012 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use Sabre\Xml\Element; use Sabre\Xml\Reader; use Sabre\Xml\Writer; /** * ShareTypeList property * * This property contains multiple "share-type" elements, each containing a share type. */ class ShareTypeList implements Element { const NS_OWNCLOUD = 'http://owncloud.org/ns'; /** * Share types * * @var int[] */ private $shareTypes; /** * @param int[] $shareTypes */ public function __construct($shareTypes) { $this->shareTypes = $shareTypes; } /** * Returns the share types * * @return int[] */ public function getShareTypes() { return $this->shareTypes; } /** * The deserialize method is called during xml parsing. * * @param Reader $reader * @return mixed */ static function xmlDeserialize(Reader $reader) { $shareTypes = []; $tree = $reader->parseInnerTree(); if ($tree === null) { return null; } foreach ($tree as $elem) { if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}share-type') { $shareTypes[] = (int)$elem['value']; } } return new self($shareTypes); } /** * The xmlSerialize metod is called during xml writing. * * @param Writer $writer * @return void */ function xmlSerialize(Writer $writer) { foreach ($this->shareTypes as $shareType) { $writer->writeElement('{' . self::NS_OWNCLOUD . '}share-type', $shareType); } } } Connector/Sabre/BearerAuth.php 0000604 00000005151 15247164651 0012276 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\Connector\Sabre; use OCP\IRequest; use OCP\ISession; use OCP\IUserSession; use Sabre\DAV\Auth\Backend\AbstractBearer; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; class BearerAuth extends AbstractBearer { /** @var IUserSession */ private $userSession; /** @var ISession */ private $session; /** @var IRequest */ private $request; /** @var string */ private $principalPrefix; /** * @param IUserSession $userSession * @param ISession $session * @param string $principalPrefix * @param IRequest $request */ public function __construct(IUserSession $userSession, ISession $session, IRequest $request, $principalPrefix = 'principals/users/') { $this->userSession = $userSession; $this->session = $session; $this->request = $request; $this->principalPrefix = $principalPrefix; // setup realm $defaults = new \OCP\Defaults(); $this->realm = $defaults->getName(); } private function setupUserFs($userId) { \OC_Util::setupFS($userId); $this->session->close(); return $this->principalPrefix . $userId; } /** * {@inheritdoc} */ public function validateBearerToken($bearerToken) { \OC_Util::setupFS(); if(!$this->userSession->isLoggedIn()) { $this->userSession->tryTokenLogin($this->request); } if($this->userSession->isLoggedIn()) { return $this->setupUserFs($this->userSession->getUser()->getUID()); } return false; } /** * \Sabre\DAV\Auth\Backend\AbstractBearer::challenge sets an WWW-Authenticate * header which some DAV clients can't handle. Thus we override this function * and make it simply return a 401. * * @param RequestInterface $request * @param ResponseInterface $response */ public function challenge(RequestInterface $request, ResponseInterface $response) { $response->setStatus(401); } } Connector/Sabre/TagList.php 0000604 00000006162 15247164651 0011626 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use Sabre\Xml\Element; use Sabre\Xml\Reader; use Sabre\Xml\Writer; /** * TagList property * * This property contains multiple "tag" elements, each containing a tag name. */ class TagList implements Element { const NS_OWNCLOUD = 'http://owncloud.org/ns'; /** * tags * * @var array */ private $tags; /** * @param array $tags */ public function __construct(array $tags) { $this->tags = $tags; } /** * Returns the tags * * @return array */ public function getTags() { return $this->tags; } /** * The deserialize method is called during xml parsing. * * This method is called statictly, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * You are responsible for advancing the reader to the next element. Not * doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * * @param Reader $reader * @return mixed */ static function xmlDeserialize(Reader $reader) { $tags = []; $tree = $reader->parseInnerTree(); if ($tree === null) { return null; } foreach ($tree as $elem) { if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}tag') { $tags[] = $elem['value']; } } return new self($tags); } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Writer $writer) { foreach ($this->tags as $tag) { $writer->writeElement('{' . self::NS_OWNCLOUD . '}tag', $tag); } } } Connector/Sabre/CommentPropertiesPlugin.php 0000604 00000011115 15247164651 0015107 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCP\Comments\ICommentsManager; use OCP\IUserSession; use Sabre\DAV\PropFind; use Sabre\DAV\ServerPlugin; class CommentPropertiesPlugin extends ServerPlugin { const PROPERTY_NAME_HREF = '{http://owncloud.org/ns}comments-href'; const PROPERTY_NAME_COUNT = '{http://owncloud.org/ns}comments-count'; const PROPERTY_NAME_UNREAD = '{http://owncloud.org/ns}comments-unread'; /** @var \Sabre\DAV\Server */ protected $server; /** @var ICommentsManager */ private $commentsManager; /** @var IUserSession */ private $userSession; private $cachedUnreadCount = []; private $cachedFolders = []; public function __construct(ICommentsManager $commentsManager, IUserSession $userSession) { $this->commentsManager = $commentsManager; $this->userSession = $userSession; } /** * This initializes the plugin. * * This function is called by Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $this->server->on('propFind', array($this, 'handleGetProperties')); } /** * Adds tags and favorites properties to the response, * if requested. * * @param PropFind $propFind * @param \Sabre\DAV\INode $node * @return void */ public function handleGetProperties( PropFind $propFind, \Sabre\DAV\INode $node ) { if (!($node instanceof File) && !($node instanceof Directory)) { return; } // need prefetch ? if ($node instanceof \OCA\DAV\Connector\Sabre\Directory && $propFind->getDepth() !== 0 && !is_null($propFind->getStatus(self::PROPERTY_NAME_UNREAD)) ) { $unreadCounts = $this->commentsManager->getNumberOfUnreadCommentsForFolder($node->getId(), $this->userSession->getUser()); $this->cachedFolders[] = $node->getPath(); foreach ($unreadCounts as $id => $count) { $this->cachedUnreadCount[$id] = $count; } } $propFind->handle(self::PROPERTY_NAME_COUNT, function() use ($node) { return $this->commentsManager->getNumberOfCommentsForObject('files', strval($node->getId())); }); $propFind->handle(self::PROPERTY_NAME_HREF, function() use ($node) { return $this->getCommentsLink($node); }); $propFind->handle(self::PROPERTY_NAME_UNREAD, function() use ($node) { if (isset($this->cachedUnreadCount[$node->getId()])) { return $this->cachedUnreadCount[$node->getId()]; } else { list($parentPath,) = \Sabre\Uri\split($node->getPath()); if ($parentPath === '') { $parentPath = '/'; } // if we already cached the folder this file is in we know there are no shares for this file if (array_search($parentPath, $this->cachedFolders) === false) { return $this->getUnreadCount($node); } else { return 0; } } }); } /** * returns a reference to the comments node * * @param Node $node * @return mixed|string */ public function getCommentsLink(Node $node) { $href = $this->server->getBaseUri(); $entryPoint = strpos($href, '/remote.php/'); if($entryPoint === false) { // in case we end up somewhere else, unexpectedly. return null; } $commentsPart = 'dav/comments/files/' . rawurldecode($node->getId()); $href = substr_replace($href, $commentsPart, $entryPoint + strlen('/remote.php/')); return $href; } /** * returns the number of unread comments for the currently logged in user * on the given file or directory node * * @param Node $node * @return Int|null */ public function getUnreadCount(Node $node) { $user = $this->userSession->getUser(); if(is_null($user)) { return null; } $lastRead = $this->commentsManager->getReadMark('files', strval($node->getId()), $user); return $this->commentsManager->getNumberOfCommentsForObject('files', strval($node->getId()), $lastRead); } } Connector/Sabre/ServerFactory.php 0000604 00000015043 15247164651 0013053 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OC\Files\Node\Folder; use OCA\DAV\Files\BrowserErrorPagePlugin; use OCP\Files\Mount\IMountManager; use OCP\IConfig; use OCP\IDBConnection; use OCP\ILogger; use OCP\IPreview; use OCP\IRequest; use OCP\ITagManager; use OCP\IUserSession; use Sabre\DAV\Auth\Backend\BackendInterface; use Sabre\DAV\Auth\Plugin; class ServerFactory { /** @var IConfig */ private $config; /** @var ILogger */ private $logger; /** @var IDBConnection */ private $databaseConnection; /** @var IUserSession */ private $userSession; /** @var IMountManager */ private $mountManager; /** @var ITagManager */ private $tagManager; /** @var IRequest */ private $request; /** @var IPreview */ private $previewManager; /** * @param IConfig $config * @param ILogger $logger * @param IDBConnection $databaseConnection * @param IUserSession $userSession * @param IMountManager $mountManager * @param ITagManager $tagManager * @param IRequest $request * @param IPreview $previewManager */ public function __construct( IConfig $config, ILogger $logger, IDBConnection $databaseConnection, IUserSession $userSession, IMountManager $mountManager, ITagManager $tagManager, IRequest $request, IPreview $previewManager ) { $this->config = $config; $this->logger = $logger; $this->databaseConnection = $databaseConnection; $this->userSession = $userSession; $this->mountManager = $mountManager; $this->tagManager = $tagManager; $this->request = $request; $this->previewManager = $previewManager; } /** * @param string $baseUri * @param string $requestUri * @param Plugin $authPlugin * @param callable $viewCallBack callback that should return the view for the dav endpoint * @return Server */ public function createServer($baseUri, $requestUri, Plugin $authPlugin, callable $viewCallBack) { // Fire up server $objectTree = new \OCA\DAV\Connector\Sabre\ObjectTree(); $server = new \OCA\DAV\Connector\Sabre\Server($objectTree); // Set URL explicitly due to reverse-proxy situations $server->httpRequest->setUrl($requestUri); $server->setBaseUri($baseUri); // Load plugins $server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin($this->config)); $server->addPlugin($authPlugin); // FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / $server->addPlugin(new \OCA\DAV\Connector\Sabre\DummyGetResponsePlugin()); $server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin()); // Some WebDAV clients do require Class 2 WebDAV support (locking), since // we do not provide locking we emulate it using a fake locking plugin. if($this->request->isUserAgent([ '/WebDAVFS/', '/Microsoft Office OneNote 2013/', '/Microsoft-WebDAV-MiniRedir/', ])) { $server->addPlugin(new \OCA\DAV\Connector\Sabre\FakeLockerPlugin()); } if (BrowserErrorPagePlugin::isBrowserRequest($this->request)) { $server->addPlugin(new BrowserErrorPagePlugin()); } // wait with registering these until auth is handled and the filesystem is setup $server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) { // ensure the skeleton is copied $userFolder = \OC::$server->getUserFolder(); /** @var \OC\Files\View $view */ $view = $viewCallBack($server); if ($userFolder instanceof Folder && $userFolder->getPath() === $view->getRoot()) { $rootInfo = $userFolder; } else { $rootInfo = $view->getFileInfo(''); } // Create Nextcloud Dir if ($rootInfo->getType() === 'dir') { $root = new \OCA\DAV\Connector\Sabre\Directory($view, $rootInfo, $objectTree); } else { $root = new \OCA\DAV\Connector\Sabre\File($view, $rootInfo); } $objectTree->init($root, $view, $this->mountManager); $server->addPlugin( new \OCA\DAV\Connector\Sabre\FilesPlugin( $objectTree, $this->config, $this->request, $this->previewManager, false, !$this->config->getSystemValue('debug', false) ) ); $server->addPlugin(new \OCA\DAV\Connector\Sabre\QuotaPlugin($view)); if($this->userSession->isLoggedIn()) { $server->addPlugin(new \OCA\DAV\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\SharesPlugin( $objectTree, $this->userSession, $userFolder, \OC::$server->getShareManager() )); $server->addPlugin(new \OCA\DAV\Connector\Sabre\CommentPropertiesPlugin(\OC::$server->getCommentsManager(), $this->userSession)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesReportPlugin( $objectTree, $view, \OC::$server->getSystemTagManager(), \OC::$server->getSystemTagObjectMapper(), \OC::$server->getTagManager(), $this->userSession, \OC::$server->getGroupManager(), $userFolder )); // custom properties plugin must be the last one $server->addPlugin( new \Sabre\DAV\PropertyStorage\Plugin( new \OCA\DAV\Connector\Sabre\CustomPropertiesBackend( $objectTree, $this->databaseConnection, $this->userSession->getUser() ) ) ); } $server->addPlugin(new \OCA\DAV\Connector\Sabre\CopyEtagHeaderPlugin()); }, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request return $server; } } Connector/Sabre/MaintenancePlugin.php 0000604 00000004515 15247164651 0013660 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCP\IConfig; use Sabre\DAV\Exception\ServiceUnavailable; use Sabre\DAV\ServerPlugin; class MaintenancePlugin extends ServerPlugin { /** @var IConfig */ private $config; /** * Reference to main server object * * @var Server */ private $server; /** * @param IConfig $config */ public function __construct(IConfig $config = null) { $this->config = $config; if (is_null($config)) { $this->config = \OC::$server->getConfig(); } } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $this->server->on('beforeMethod', array($this, 'checkMaintenanceMode'), 1); } /** * This method is called before any HTTP method and returns http status code 503 * in case the system is in maintenance mode. * * @throws ServiceUnavailable * @return bool */ public function checkMaintenanceMode() { if ($this->config->getSystemValue('maintenance', false)) { throw new ServiceUnavailable('System in maintenance mode.'); } if (\OC::checkUpgrade(false)) { throw new ServiceUnavailable('Upgrade needed'); } return true; } } Connector/Sabre/SharesPlugin.php 0000604 00000012627 15247164651 0012666 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use \Sabre\DAV\PropFind; use OCP\IUserSession; use OCP\Share\IShare; /** * Sabre Plugin to provide share-related properties */ class SharesPlugin extends \Sabre\DAV\ServerPlugin { const NS_OWNCLOUD = 'http://owncloud.org/ns'; const SHARETYPES_PROPERTYNAME = '{http://owncloud.org/ns}share-types'; /** * Reference to main server object * * @var \Sabre\DAV\Server */ private $server; /** * @var \OCP\Share\IManager */ private $shareManager; /** * @var \Sabre\DAV\Tree */ private $tree; /** * @var string */ private $userId; /** * @var \OCP\Files\Folder */ private $userFolder; /** * @var IShare[] */ private $cachedShareTypes; private $cachedFolders = []; /** * @param \Sabre\DAV\Tree $tree tree * @param IUserSession $userSession user session * @param \OCP\Files\Folder $userFolder user home folder * @param \OCP\Share\IManager $shareManager share manager */ public function __construct( \Sabre\DAV\Tree $tree, IUserSession $userSession, \OCP\Files\Folder $userFolder, \OCP\Share\IManager $shareManager ) { $this->tree = $tree; $this->shareManager = $shareManager; $this->userFolder = $userFolder; $this->userId = $userSession->getUser()->getUID(); $this->cachedShareTypes = []; } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server */ public function initialize(\Sabre\DAV\Server $server) { $server->xml->namespacesMap[self::NS_OWNCLOUD] = 'oc'; $server->xml->elementMap[self::SHARETYPES_PROPERTYNAME] = 'OCA\\DAV\\Connector\\Sabre\\ShareTypeList'; $server->protectedProperties[] = self::SHARETYPES_PROPERTYNAME; $this->server = $server; $this->server->on('propFind', array($this, 'handleGetProperties')); } /** * Return a list of share types for outgoing shares * * @param \OCP\Files\Node $node file node * * @return int[] array of share types */ private function getShareTypes(\OCP\Files\Node $node) { $shareTypes = []; $requestedShareTypes = [ \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL, ]; foreach ($requestedShareTypes as $requestedShareType) { // one of each type is enough to find out about the types $shares = $this->shareManager->getSharesBy( $this->userId, $requestedShareType, $node, false, 1 ); if (!empty($shares)) { $shareTypes[] = $requestedShareType; } } return $shareTypes; } private function getSharesTypesInFolder(\OCP\Files\Folder $node) { $shares = $this->shareManager->getSharesInFolder( $this->userId, $node, true ); $shareTypesByFileId = []; foreach($shares as $fileId => $sharesForFile) { $types = array_map(function(IShare $share) { return $share->getShareType(); }, $sharesForFile); $types = array_unique($types); sort($types); $shareTypesByFileId[$fileId] = $types; } return $shareTypesByFileId; } /** * Adds shares to propfind response * * @param PropFind $propFind propfind object * @param \Sabre\DAV\INode $sabreNode sabre node */ public function handleGetProperties( PropFind $propFind, \Sabre\DAV\INode $sabreNode ) { if (!($sabreNode instanceof \OCA\DAV\Connector\Sabre\Node)) { return; } // need prefetch ? if ($sabreNode instanceof \OCA\DAV\Connector\Sabre\Directory && $propFind->getDepth() !== 0 && !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME)) ) { $folderNode = $this->userFolder->get($sabreNode->getPath()); $childShares = $this->getSharesTypesInFolder($folderNode); $this->cachedFolders[] = $sabreNode->getPath(); $this->cachedShareTypes[$folderNode->getId()] = $this->getShareTypes($folderNode); foreach ($childShares as $id => $shares) { $this->cachedShareTypes[$id] = $shares; } } $propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode) { if (isset($this->cachedShareTypes[$sabreNode->getId()])) { $shareTypes = $this->cachedShareTypes[$sabreNode->getId()]; } else { list($parentPath,) = \Sabre\Uri\split($sabreNode->getPath()); if ($parentPath === '') { $parentPath = '/'; } // if we already cached the folder this file is in we know there are no shares for this file if (array_search($parentPath, $this->cachedFolders) === false) { $node = $this->userFolder->get($sabreNode->getPath()); $shareTypes = $this->getShareTypes($node); } else { return []; } } return new ShareTypeList($shareTypes); }); } } Connector/Sabre/FilesReportPlugin.php 0000604 00000024267 15247164651 0013702 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OC\Files\View; use Sabre\DAV\Exception\PreconditionFailed; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\ServerPlugin; use Sabre\DAV\Tree; use Sabre\DAV\Xml\Element\Response; use Sabre\DAV\Xml\Response\MultiStatus; use Sabre\DAV\PropFind; use OCP\SystemTag\ISystemTagObjectMapper; use OCP\IUserSession; use OCP\Files\Folder; use OCP\IGroupManager; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\TagNotFoundException; use OCP\ITagManager; class FilesReportPlugin extends ServerPlugin { // namespace const NS_OWNCLOUD = 'http://owncloud.org/ns'; const REPORT_NAME = '{http://owncloud.org/ns}filter-files'; const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag'; /** * Reference to main server object * * @var \Sabre\DAV\Server */ private $server; /** * @var Tree */ private $tree; /** * @var View */ private $fileView; /** * @var ISystemTagManager */ private $tagManager; /** * @var ISystemTagObjectMapper */ private $tagMapper; /** * Manager for private tags * * @var ITagManager */ private $fileTagger; /** * @var IUserSession */ private $userSession; /** * @var IGroupManager */ private $groupManager; /** * @var Folder */ private $userFolder; /** * @param Tree $tree * @param View $view * @param ISystemTagManager $tagManager * @param ISystemTagObjectMapper $tagMapper * @param ITagManager $fileTagger manager for private tags * @param IUserSession $userSession * @param IGroupManager $groupManager * @param Folder $userFolder */ public function __construct(Tree $tree, View $view, ISystemTagManager $tagManager, ISystemTagObjectMapper $tagMapper, ITagManager $fileTagger, IUserSession $userSession, IGroupManager $groupManager, Folder $userFolder ) { $this->tree = $tree; $this->fileView = $view; $this->tagManager = $tagManager; $this->tagMapper = $tagMapper; $this->fileTagger = $fileTagger; $this->userSession = $userSession; $this->groupManager = $groupManager; $this->userFolder = $userFolder; } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; $this->server = $server; $this->server->on('report', array($this, 'onReport')); } /** * Returns a list of reports this plugin supports. * * This will be used in the {DAV:}supported-report-set property. * * @param string $uri * @return array */ public function getSupportedReportSet($uri) { return [self::REPORT_NAME]; } /** * REPORT operations to look for files * * @param string $reportName * @param $report * @param string $uri * @return bool * @throws BadRequest * @throws PreconditionFailed * @internal param $ [] $report */ public function onReport($reportName, $report, $uri) { $reportTargetNode = $this->server->tree->getNodeForPath($uri); if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) { return; } $ns = '{' . $this::NS_OWNCLOUD . '}'; $requestedProps = []; $filterRules = []; // parse report properties and gather filter info foreach ($report as $reportProps) { $name = $reportProps['name']; if ($name === $ns . 'filter-rules') { $filterRules = $reportProps['value']; } else if ($name === '{DAV:}prop') { // propfind properties foreach ($reportProps['value'] as $propVal) { $requestedProps[] = $propVal['name']; } } } if (empty($filterRules)) { // an empty filter would return all existing files which would be slow throw new BadRequest('Missing filter-rule block in request'); } // gather all file ids matching filter try { $resultFileIds = $this->processFilterRules($filterRules); } catch (TagNotFoundException $e) { throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e); } // find sabre nodes by file id, restricted to the root node path $results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds); $filesUri = $this->getFilesBaseUri($uri, $reportTargetNode->getPath()); $responses = $this->prepareResponses($filesUri, $requestedProps, $results); $xml = $this->server->xml->write( '{DAV:}multistatus', new MultiStatus($responses) ); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setBody($xml); return false; } /** * Returns the base uri of the files root by removing * the subpath from the URI * * @param string $uri URI from this request * @param string $subPath subpath to remove from the URI * * @return string files base uri */ private function getFilesBaseUri($uri, $subPath) { $uri = trim($uri, '/'); $subPath = trim($subPath, '/'); if (empty($subPath)) { $filesUri = $uri; } else { $filesUri = substr($uri, 0, strlen($uri) - strlen($subPath)); } $filesUri = trim($filesUri, '/'); if (empty($filesUri)) { return ''; } return '/' . $filesUri; } /** * Find file ids matching the given filter rules * * @param array $filterRules * @return array array of unique file id results * * @throws TagNotFoundException whenever a tag was not found */ protected function processFilterRules($filterRules) { $ns = '{' . $this::NS_OWNCLOUD . '}'; $resultFileIds = null; $systemTagIds = []; $favoriteFilter = null; foreach ($filterRules as $filterRule) { if ($filterRule['name'] === $ns . 'systemtag') { $systemTagIds[] = $filterRule['value']; } if ($filterRule['name'] === $ns . 'favorite') { $favoriteFilter = true; } } if ($favoriteFilter !== null) { $resultFileIds = $this->fileTagger->load('files')->getFavorites(); if (empty($resultFileIds)) { return []; } } if (!empty($systemTagIds)) { $fileIds = $this->getSystemTagFileIds($systemTagIds); if (empty($resultFileIds)) { $resultFileIds = $fileIds; } else { $resultFileIds = array_intersect($fileIds, $resultFileIds); } } return $resultFileIds; } private function getSystemTagFileIds($systemTagIds) { $resultFileIds = null; // check user permissions, if applicable if (!$this->isAdmin()) { // check visibility/permission $tags = $this->tagManager->getTagsByIds($systemTagIds); $unknownTagIds = []; foreach ($tags as $tag) { if (!$tag->isUserVisible()) { $unknownTagIds[] = $tag->getId(); } } if (!empty($unknownTagIds)) { throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found'); } } // fetch all file ids and intersect them foreach ($systemTagIds as $systemTagId) { $fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files'); if (empty($fileIds)) { // This tag has no files, nothing can ever show up return []; } // first run ? if ($resultFileIds === null) { $resultFileIds = $fileIds; } else { $resultFileIds = array_intersect($resultFileIds, $fileIds); } if (empty($resultFileIds)) { // Empty intersection, nothing can show up anymore return []; } } return $resultFileIds; } /** * Prepare propfind response for the given nodes * * @param string $filesUri $filesUri URI leading to root of the files URI, * with a leading slash but no trailing slash * @param string[] $requestedProps requested properties * @param Node[] nodes nodes for which to fetch and prepare responses * @return Response[] */ public function prepareResponses($filesUri, $requestedProps, $nodes) { $responses = []; foreach ($nodes as $node) { $propFind = new PropFind($filesUri . $node->getPath(), $requestedProps); $this->server->getPropertiesByNode($propFind, $node); // copied from Sabre Server's getPropertiesForPath $result = $propFind->getResultForMultiStatus(); $result['href'] = $propFind->getPath(); $resourceType = $this->server->getResourceTypeForNode($node); if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { $result['href'] .= '/'; } $responses[] = new Response( rtrim($this->server->getBaseUri(), '/') . $filesUri . $node->getPath(), $result, 200 ); } return $responses; } /** * Find Sabre nodes by file ids * * @param Node $rootNode root node for search * @param array $fileIds file ids * @return Node[] array of Sabre nodes */ public function findNodesByFileIds($rootNode, $fileIds) { $folder = $this->userFolder; if (trim($rootNode->getPath(), '/') !== '') { $folder = $folder->get($rootNode->getPath()); } $results = []; foreach ($fileIds as $fileId) { $entry = $folder->getById($fileId); if ($entry) { $entry = current($entry); if ($entry instanceof \OCP\Files\File) { $results[] = new File($this->fileView, $entry); } else if ($entry instanceof \OCP\Files\Folder) { $results[] = new Directory($this->fileView, $entry); } } } return $results; } /** * Returns whether the currently logged in user is an administrator */ private function isAdmin() { $user = $this->userSession->getUser(); if ($user !== null) { return $this->groupManager->isAdmin($user->getUID()); } return false; } } Connector/Sabre/CopyEtagHeaderPlugin.php 0000604 00000004454 15247164651 0014264 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use \Sabre\HTTP\RequestInterface; use \Sabre\HTTP\ResponseInterface; /** * Copies the "Etag" header to "OC-Etag" after any request. * This is a workaround for setups that automatically strip * or mangle Etag headers. */ class CopyEtagHeaderPlugin extends \Sabre\DAV\ServerPlugin { /** @var \Sabre\DAV\Server */ private $server; /** * This initializes the plugin. * * @param \Sabre\DAV\Server $server Sabre server * * @return void */ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $server->on('afterMethod', [$this, 'afterMethod']); $server->on('afterMove', [$this, 'afterMove']); } /** * After method, copy the "Etag" header to "OC-Etag" header. * * @param RequestInterface $request request * @param ResponseInterface $response response */ public function afterMethod(RequestInterface $request, ResponseInterface $response) { $eTag = $response->getHeader('Etag'); if (!empty($eTag)) { $response->setHeader('OC-ETag', $eTag); } } /** * Called after a node is moved. * * This allows the backend to move all the associated properties. * * @param string $source * @param string $destination * @return void */ function afterMove($source, $destination) { $node = $this->server->tree->getNodeForPath($destination); if ($node instanceof File) { $eTag = $node->getETag(); $this->server->httpResponse->setHeader('OC-ETag', $eTag); $this->server->httpResponse->setHeader('ETag', $eTag); } } } Connector/Sabre/ObjectTree.php 0000604 00000016053 15247164651 0012305 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCA\DAV\Connector\Sabre\Exception\Forbidden; use OCA\DAV\Connector\Sabre\Exception\InvalidPath; use OCA\DAV\Connector\Sabre\Exception\FileLocked; use OC\Files\FileInfo; use OC\Files\Mount\MoveableMount; use OCP\Files\ForbiddenException; use OCP\Files\StorageInvalidException; use OCP\Files\StorageNotAvailableException; use OCP\Lock\LockedException; class ObjectTree extends \Sabre\DAV\Tree { /** * @var \OC\Files\View */ protected $fileView; /** * @var \OCP\Files\Mount\IMountManager */ protected $mountManager; /** * Creates the object */ public function __construct() { } /** * @param \Sabre\DAV\INode $rootNode * @param \OC\Files\View $view * @param \OCP\Files\Mount\IMountManager $mountManager */ public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) { $this->rootNode = $rootNode; $this->fileView = $view; $this->mountManager = $mountManager; } /** * If the given path is a chunked file name, converts it * to the real file name. Only applies if the OC-CHUNKED header * is present. * * @param string $path chunk file path to convert * * @return string path to real file */ private function resolveChunkFile($path) { if (isset($_SERVER['HTTP_OC_CHUNKED'])) { // resolve to real file name to find the proper node list($dir, $name) = \Sabre\HTTP\URLUtil::splitPath($path); if ($dir == '/' || $dir == '.') { $dir = ''; } $info = \OC_FileChunking::decodeName($name); // only replace path if it was really the chunked file if (isset($info['transferid'])) { // getNodePath is called for multiple nodes within a chunk // upload call $path = $dir . '/' . $info['name']; $path = ltrim($path, '/'); } } return $path; } public function cacheNode(Node $node) { $this->cache[trim($node->getPath(), '/')] = $node; } /** * Returns the INode object for the requested path * * @param string $path * @return \Sabre\DAV\INode * @throws InvalidPath * @throws \Sabre\DAV\Exception\Locked * @throws \Sabre\DAV\Exception\NotFound * @throws \Sabre\DAV\Exception\ServiceUnavailable */ public function getNodeForPath($path) { if (!$this->fileView) { throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup'); } $path = trim($path, '/'); if (isset($this->cache[$path])) { return $this->cache[$path]; } if ($path) { try { $this->fileView->verifyPath($path, basename($path)); } catch (\OCP\Files\InvalidPathException $ex) { throw new InvalidPath($ex->getMessage()); } } // Is it the root node? if (!strlen($path)) { return $this->rootNode; } if (pathinfo($path, PATHINFO_EXTENSION) === 'part') { // read from storage $absPath = $this->fileView->getAbsolutePath($path); $mount = $this->fileView->getMount($path); $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($absPath); if ($storage && $storage->file_exists($internalPath)) { /** * @var \OC\Files\Storage\Storage $storage */ // get data directly $data = $storage->getMetaData($internalPath); $info = new FileInfo($absPath, $storage, $internalPath, $data, $mount); } else { $info = null; } } else { // resolve chunk file name to real name, if applicable $path = $this->resolveChunkFile($path); // read from cache try { $info = $this->fileView->getFileInfo($path); } catch (StorageNotAvailableException $e) { throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage is temporarily not available'); } catch (StorageInvalidException $e) { throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid'); } catch (LockedException $e) { throw new \Sabre\DAV\Exception\Locked(); } catch (ForbiddenException $e) { throw new \Sabre\DAV\Exception\Forbidden(); } } if (!$info) { throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located'); } if ($info->getType() === 'dir') { $node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this); } else { $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info); } $this->cache[$path] = $node; return $node; } /** * Copies a file or directory. * * This method must work recursively and delete the destination * if it exists * * @param string $source * @param string $destination * @throws FileLocked * @throws Forbidden * @throws InvalidPath * @throws \Exception * @throws \Sabre\DAV\Exception\Forbidden * @throws \Sabre\DAV\Exception\Locked * @throws \Sabre\DAV\Exception\NotFound * @throws \Sabre\DAV\Exception\ServiceUnavailable * @return void */ public function copy($source, $destination) { if (!$this->fileView) { throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup'); } $info = $this->fileView->getFileInfo(dirname($destination)); if ($this->fileView->file_exists($destination)) { $destinationPermission = $info && $info->isUpdateable(); } else { $destinationPermission = $info && $info->isCreatable(); } if (!$destinationPermission) { throw new Forbidden('No permissions to copy object.'); } // this will trigger existence check $this->getNodeForPath($source); list($destinationDir, $destinationName) = \Sabre\HTTP\URLUtil::splitPath($destination); try { $this->fileView->verifyPath($destinationDir, $destinationName); } catch (\OCP\Files\InvalidPathException $ex) { throw new InvalidPath($ex->getMessage()); } try { $this->fileView->copy($source, $destination); } catch (StorageNotAvailableException $e) { throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage()); } catch (ForbiddenException $ex) { throw new Forbidden($ex->getMessage(), $ex->getRetry()); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination); $this->markDirty($destinationDir); } } Connector/Sabre/Exception/PasswordLoginForbidden.php 0000604 00000003060 15247164651 0016617 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre\Exception; use DOMElement; use Sabre\DAV\Server; use Sabre\DAV\Exception\NotAuthenticated; class PasswordLoginForbidden extends NotAuthenticated { const NS_OWNCLOUD = 'http://owncloud.org/ns'; public function getHTTPCode() { return 401; } /** * This method allows the exception to include additional information * into the WebDAV error response * * @param Server $server * @param DOMElement $errorNode * @return void */ public function serialize(Server $server, DOMElement $errorNode) { // set ownCloud namespace $errorNode->setAttribute('xmlns:o', self::NS_OWNCLOUD); $error = $errorNode->ownerDocument->createElementNS('o:', 'o:hint', 'password login forbidden'); $errorNode->appendChild($error); } } Connector/Sabre/Exception/EntityTooLarge.php 0000604 00000002304 15247164651 0015120 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre\Exception; /** * Entity Too Large * * This exception is thrown whenever a user tries to upload a file which exceeds hard limitations * */ class EntityTooLarge extends \Sabre\DAV\Exception { /** * Returns the HTTP status code for this exception * * @return int */ public function getHTTPCode() { return 413; } } Connector/Sabre/Exception/FileLocked.php 0000604 00000002731 15247164651 0014214 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Owen Winkler <a_github@midnightcircus.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre\Exception; use Exception; class FileLocked extends \Sabre\DAV\Exception { public function __construct($message = "", $code = 0, Exception $previous = null) { if($previous instanceof \OCP\Files\LockNotAcquiredException) { $message = sprintf('Target file %s is locked by another process.', $previous->path); } parent::__construct($message, $code, $previous); } /** * Returns the HTTP status code for this exception * * @return int */ public function getHTTPCode() { return 423; } } Connector/Sabre/Exception/InvalidPath.php 0000604 00000003672 15247164651 0014423 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre\Exception; use Sabre\DAV\Exception; class InvalidPath extends Exception { const NS_OWNCLOUD = 'http://owncloud.org/ns'; /** * @var bool */ private $retry; /** * @param string $message * @param bool $retry */ public function __construct($message, $retry = false) { parent::__construct($message); $this->retry = $retry; } /** * Returns the HTTP status code for this exception * * @return int */ public function getHTTPCode() { return 400; } /** * This method allows the exception to include additional information * into the WebDAV error response * * @param \Sabre\DAV\Server $server * @param \DOMElement $errorNode * @return void */ public function serialize(\Sabre\DAV\Server $server,\DOMElement $errorNode) { // set ownCloud namespace $errorNode->setAttribute('xmlns:o', self::NS_OWNCLOUD); // adding the retry node $error = $errorNode->ownerDocument->createElementNS('o:','o:retry', var_export($this->retry, true)); $errorNode->appendChild($error); // adding the message node $error = $errorNode->ownerDocument->createElementNS('o:','o:reason', $this->getMessage()); $errorNode->appendChild($error); } } Connector/Sabre/Exception/UnsupportedMediaType.php 0000604 00000002332 15247164651 0016342 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre\Exception; /** * Unsupported Media Type * * This exception is thrown whenever a user tries to upload a file which holds content which is not allowed * */ class UnsupportedMediaType extends \Sabre\DAV\Exception { /** * Returns the HTTP status code for this exception * * @return int */ public function getHTTPCode() { return 415; } } Connector/Sabre/Exception/Forbidden.php 0000604 00000003563 15247164651 0014113 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre\Exception; class Forbidden extends \Sabre\DAV\Exception\Forbidden { const NS_OWNCLOUD = 'http://owncloud.org/ns'; /** * @var bool */ private $retry; /** * @param string $message * @param bool $retry * @param \Exception $previous */ public function __construct($message, $retry = false, \Exception $previous = null) { parent::__construct($message, 0, $previous); $this->retry = $retry; } /** * This method allows the exception to include additional information * into the WebDAV error response * * @param \Sabre\DAV\Server $server * @param \DOMElement $errorNode * @return void */ public function serialize(\Sabre\DAV\Server $server,\DOMElement $errorNode) { // set ownCloud namespace $errorNode->setAttribute('xmlns:o', self::NS_OWNCLOUD); // adding the retry node $error = $errorNode->ownerDocument->createElementNS('o:','o:retry', var_export($this->retry, true)); $errorNode->appendChild($error); // adding the message node $error = $errorNode->ownerDocument->createElementNS('o:','o:reason', $this->getMessage()); $errorNode->appendChild($error); } } Connector/Sabre/File.php 0000604 00000046711 15247164651 0011142 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Owen Winkler <a_github@midnightcircus.com> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OC\Files\Filesystem; use OCA\DAV\Connector\Sabre\Exception\EntityTooLarge; use OCA\DAV\Connector\Sabre\Exception\FileLocked; use OCA\DAV\Connector\Sabre\Exception\Forbidden as DAVForbiddenException; use OCA\DAV\Connector\Sabre\Exception\UnsupportedMediaType; use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\Files\EntityTooLargeException; use OCP\Files\ForbiddenException; use OCP\Files\InvalidContentException; use OCP\Files\InvalidPathException; use OCP\Files\LockNotAcquiredException; use OCP\Files\NotPermittedException; use OCP\Files\StorageNotAvailableException; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; use Sabre\DAV\Exception; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotImplemented; use Sabre\DAV\Exception\ServiceUnavailable; use Sabre\DAV\IFile; use Sabre\DAV\Exception\NotFound; class File extends Node implements IFile { /** * Updates the data * * The data argument is a readable stream resource. * * After a successful put operation, you may choose to return an ETag. The * etag must always be surrounded by double-quotes. These quotes must * appear in the actual string you're returning. * * Clients may use the ETag from a PUT request to later on make sure that * when they update the file, the contents haven't changed in the mean * time. * * If you don't plan to store the file byte-by-byte, and you return a * different object on a subsequent GET you are strongly recommended to not * return an ETag, and just return null. * * @param resource $data * * @throws Forbidden * @throws UnsupportedMediaType * @throws BadRequest * @throws Exception * @throws EntityTooLarge * @throws ServiceUnavailable * @throws FileLocked * @return string|null */ public function put($data) { try { $exists = $this->fileView->file_exists($this->path); if ($this->info && $exists && !$this->info->isUpdateable()) { throw new Forbidden(); } } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("File is not updatable: " . $e->getMessage()); } // verify path of the target $this->verifyPath(); // chunked handling if (isset($_SERVER['HTTP_OC_CHUNKED'])) { try { return $this->createFileChunked($data); } catch (\Exception $e) { $this->convertToSabreException($e); } } list($partStorage) = $this->fileView->resolvePath($this->path); $needsPartFile = $this->needsPartFile($partStorage) && (strlen($this->path) > 1); if ($needsPartFile) { // mark file as partial while uploading (ignored by the scanner) $partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part'; } else { // upload file directly as the final path $partFilePath = $this->path; } // the part file and target file might be on a different storage in case of a single file storage (e.g. single file share) /** @var \OC\Files\Storage\Storage $partStorage */ list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath); /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = $this->fileView->resolvePath($this->path); try { $target = $partStorage->fopen($internalPartPath, 'wb'); if ($target === false) { \OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::fopen() failed', \OCP\Util::ERROR); // because we have no clue about the cause we can only throw back a 500/Internal Server Error throw new Exception('Could not write file contents'); } list($count, $result) = \OC_Helper::streamCopy($data, $target); fclose($target); if ($result === false) { $expected = -1; if (isset($_SERVER['CONTENT_LENGTH'])) { $expected = $_SERVER['CONTENT_LENGTH']; } throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )'); } // if content length is sent by client: // double check if the file was fully received // compare expected and actual size if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') { $expected = $_SERVER['CONTENT_LENGTH']; if ($count != $expected) { throw new BadRequest('expected filesize ' . $expected . ' got ' . $count); } } } catch (\Exception $e) { if ($needsPartFile) { $partStorage->unlink($internalPartPath); } $this->convertToSabreException($e); } try { $view = \OC\Files\Filesystem::getView(); if ($view) { $run = $this->emitPreHooks($exists); } else { $run = true; } try { $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE); } catch (LockedException $e) { if ($needsPartFile) { $partStorage->unlink($internalPartPath); } throw new FileLocked($e->getMessage(), $e->getCode(), $e); } if ($needsPartFile) { // rename to correct path try { if ($run) { $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath); $fileExists = $storage->file_exists($internalPath); } if (!$run || $renameOkay === false || $fileExists === false) { \OCP\Util::writeLog('webdav', 'renaming part file to final file failed ($run: ' . ( $run ? 'true' : 'false' ) . ', $renameOkay: ' . ( $renameOkay ? 'true' : 'false' ) . ', $fileExists: ' . ( $fileExists ? 'true' : 'false' ) . ')', \OCP\Util::ERROR); throw new Exception('Could not rename part file to final file'); } } catch (ForbiddenException $ex) { throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry()); } catch (\Exception $e) { $partStorage->unlink($internalPartPath); $this->convertToSabreException($e); } } // since we skipped the view we need to scan and emit the hooks ourselves $storage->getUpdater()->update($internalPath); try { $this->changeLock(ILockingProvider::LOCK_SHARED); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } // allow sync clients to send the mtime along in a header $request = \OC::$server->getRequest(); if (isset($request->server['HTTP_X_OC_MTIME'])) { $mtimeStr = $request->server['HTTP_X_OC_MTIME']; if (!is_numeric($mtimeStr)) { throw new \InvalidArgumentException('X-OC-Mtime header must be an integer (unix timestamp).'); } $mtime = intval($mtimeStr); if ($this->fileView->touch($this->path, $mtime)) { header('X-OC-MTime: accepted'); } } if ($view) { $this->emitPostHooks($exists); } $this->refreshInfo(); if (isset($request->server['HTTP_OC_CHECKSUM'])) { $checksum = trim($request->server['HTTP_OC_CHECKSUM']); $this->fileView->putFileInfo($this->path, ['checksum' => $checksum]); $this->refreshInfo(); } else if ($this->getChecksum() !== null && $this->getChecksum() !== '') { $this->fileView->putFileInfo($this->path, ['checksum' => '']); $this->refreshInfo(); } } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage()); } return '"' . $this->info->getEtag() . '"'; } private function getPartFileBasePath($path) { $partFileInStorage = \OC::$server->getConfig()->getSystemValue('part_file_in_storage', true); if ($partFileInStorage) { return $path; } else { return md5($path); // will place it in the root of the view with a unique name } } /** * @param string $path */ private function emitPreHooks($exists, $path = null) { if (is_null($path)) { $path = $this->path; } $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path)); $run = true; if (!$exists) { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array( \OC\Files\Filesystem::signal_param_path => $hookPath, \OC\Files\Filesystem::signal_param_run => &$run, )); } else { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array( \OC\Files\Filesystem::signal_param_path => $hookPath, \OC\Files\Filesystem::signal_param_run => &$run, )); } \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array( \OC\Files\Filesystem::signal_param_path => $hookPath, \OC\Files\Filesystem::signal_param_run => &$run, )); return $run; } /** * @param string $path */ private function emitPostHooks($exists, $path = null) { if (is_null($path)) { $path = $this->path; } $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path)); if (!$exists) { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array( \OC\Files\Filesystem::signal_param_path => $hookPath )); } else { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array( \OC\Files\Filesystem::signal_param_path => $hookPath )); } \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array( \OC\Files\Filesystem::signal_param_path => $hookPath )); } /** * Returns the data * * @return resource * @throws Forbidden * @throws ServiceUnavailable */ public function get() { //throw exception if encryption is disabled but files are still encrypted try { if (!$this->info->isReadable()) { // do a if the file did not exist throw new NotFound(); } $res = $this->fileView->fopen(ltrim($this->path, '/'), 'rb'); if ($res === false) { throw new ServiceUnavailable("Could not open file"); } return $res; } catch (GenericEncryptionException $e) { // returning 503 will allow retry of the operation at a later point in time throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage()); } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("Failed to open file: " . $e->getMessage()); } catch (ForbiddenException $ex) { throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry()); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } } /** * Delete the current file * * @throws Forbidden * @throws ServiceUnavailable */ public function delete() { if (!$this->info->isDeletable()) { throw new Forbidden(); } try { if (!$this->fileView->unlink($this->path)) { // assume it wasn't possible to delete due to permissions throw new Forbidden(); } } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage()); } catch (ForbiddenException $ex) { throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry()); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } } /** * Returns the mime-type for a file * * If null is returned, we'll assume application/octet-stream * * @return string */ public function getContentType() { $mimeType = $this->info->getMimetype(); // PROPFIND needs to return the correct mime type, for consistency with the web UI if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') { return $mimeType; } return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType); } /** * @return array|false */ public function getDirectDownload() { if (\OCP\App::isEnabled('encryption')) { return []; } /** @var \OCP\Files\Storage $storage */ list($storage, $internalPath) = $this->fileView->resolvePath($this->path); if (is_null($storage)) { return []; } return $storage->getDirectDownload($internalPath); } /** * @param resource $data * @return null|string * @throws Exception * @throws BadRequest * @throws NotImplemented * @throws ServiceUnavailable */ private function createFileChunked($data) { list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($this->path); $info = \OC_FileChunking::decodeName($name); if (empty($info)) { throw new NotImplemented('Invalid chunk name'); } $chunk_handler = new \OC_FileChunking($info); $bytesWritten = $chunk_handler->store($info['index'], $data); //detect aborted upload if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') { if (isset($_SERVER['CONTENT_LENGTH'])) { $expected = $_SERVER['CONTENT_LENGTH']; if ($bytesWritten != $expected) { $chunk_handler->remove($info['index']); throw new BadRequest( 'expected filesize ' . $expected . ' got ' . $bytesWritten); } } } if ($chunk_handler->isComplete()) { list($storage,) = $this->fileView->resolvePath($path); $needsPartFile = $this->needsPartFile($storage); $partFile = null; $targetPath = $path . '/' . $info['name']; /** @var \OC\Files\Storage\Storage $targetStorage */ list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath); $exists = $this->fileView->file_exists($targetPath); try { $this->fileView->lockFile($targetPath, ILockingProvider::LOCK_SHARED); $this->emitPreHooks($exists, $targetPath); $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_EXCLUSIVE); /** @var \OC\Files\Storage\Storage $targetStorage */ list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath); if ($needsPartFile) { // we first assembly the target file as a part file $partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part'; /** @var \OC\Files\Storage\Storage $targetStorage */ list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile); $chunk_handler->file_assemble($partStorage, $partInternalPath); // here is the final atomic rename $renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath); $fileExists = $targetStorage->file_exists($targetInternalPath); if ($renameOkay === false || $fileExists === false) { \OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::rename() failed', \OCP\Util::ERROR); // only delete if an error occurred and the target file was already created if ($fileExists) { // set to null to avoid double-deletion when handling exception // stray part file $partFile = null; $targetStorage->unlink($targetInternalPath); } $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED); throw new Exception('Could not rename part file assembled from chunks'); } } else { // assemble directly into the final file $chunk_handler->file_assemble($targetStorage, $targetInternalPath); } // allow sync clients to send the mtime along in a header $request = \OC::$server->getRequest(); if (isset($request->server['HTTP_X_OC_MTIME'])) { if ($targetStorage->touch($targetInternalPath, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } // since we skipped the view we need to scan and emit the hooks ourselves $targetStorage->getUpdater()->update($targetInternalPath); $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED); $this->emitPostHooks($exists, $targetPath); // FIXME: should call refreshInfo but can't because $this->path is not the of the final file $info = $this->fileView->getFileInfo($targetPath); if (isset($request->server['HTTP_OC_CHECKSUM'])) { $checksum = trim($request->server['HTTP_OC_CHECKSUM']); $this->fileView->putFileInfo($targetPath, ['checksum' => $checksum]); } else if ($info->getChecksum() !== null && $info->getChecksum() !== '') { $this->fileView->putFileInfo($this->path, ['checksum' => '']); } $this->fileView->unlockFile($targetPath, ILockingProvider::LOCK_SHARED); return $info->getEtag(); } catch (\Exception $e) { if ($partFile !== null) { $targetStorage->unlink($targetInternalPath); } $this->convertToSabreException($e); } } return null; } /** * Returns whether a part file is needed for the given storage * or whether the file can be assembled/uploaded directly on the * target storage. * * @param \OCP\Files\Storage $storage * @return bool true if the storage needs part file handling */ private function needsPartFile($storage) { // TODO: in the future use ChunkHandler provided by storage return !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') && !$storage->instanceOfStorage('OC\Files\Storage\OwnCloud') && $storage->needsPartFile(); } /** * Convert the given exception to a SabreException instance * * @param \Exception $e * * @throws \Sabre\DAV\Exception */ private function convertToSabreException(\Exception $e) { if ($e instanceof \Sabre\DAV\Exception) { throw $e; } if ($e instanceof NotPermittedException) { // a more general case - due to whatever reason the content could not be written throw new Forbidden($e->getMessage(), 0, $e); } if ($e instanceof ForbiddenException) { // the path for the file was forbidden throw new DAVForbiddenException($e->getMessage(), $e->getRetry(), $e); } if ($e instanceof EntityTooLargeException) { // the file is too big to be stored throw new EntityTooLarge($e->getMessage(), 0, $e); } if ($e instanceof InvalidContentException) { // the file content is not permitted throw new UnsupportedMediaType($e->getMessage(), 0, $e); } if ($e instanceof InvalidPathException) { // the path for the file was not valid // TODO: find proper http status code for this case throw new Forbidden($e->getMessage(), 0, $e); } if ($e instanceof LockedException || $e instanceof LockNotAcquiredException) { // the file is currently being written to by another process throw new FileLocked($e->getMessage(), $e->getCode(), $e); } if ($e instanceof GenericEncryptionException) { // returning 503 will allow retry of the operation at a later point in time throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e); } if ($e instanceof StorageNotAvailableException) { throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e); } throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e); } /** * Get the checksum for this file * * @return string */ public function getChecksum() { return $this->info->getChecksum(); } } Connector/Sabre/DummyGetResponsePlugin.php 0000604 00000004224 15247164651 0014705 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use Sabre\HTTP\ResponseInterface; use Sabre\HTTP\RequestInterface; /** * Class DummyGetResponsePlugin is a plugin used to not show a "Not implemented" * error to clients that rely on verifying the functionality of the Nextcloud * WebDAV backend using a simple GET to /. * * This is considered a legacy behaviour and implementers should consider sending * a PROPFIND request instead to verify whether the WebDAV component is working * properly. * * FIXME: Remove once clients are all compliant. * * @package OCA\DAV\Connector\Sabre */ class DummyGetResponsePlugin extends \Sabre\DAV\ServerPlugin { /** @var \Sabre\DAV\Server */ protected $server; /** * @param \Sabre\DAV\Server $server * @return void */ function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $this->server->on('method:GET', [$this, 'httpGet'], 200); } /** * @param RequestInterface $request * @param ResponseInterface $response * @return false */ function httpGet(RequestInterface $request, ResponseInterface $response) { $string = 'This is the WebDAV interface. It can only be accessed by ' . 'WebDAV clients such as the Nextcloud desktop sync client.'; $stream = fopen('php://memory','r+'); fwrite($stream, $string); rewind($stream); $response->setStatus(200); $response->setBody($stream); return false; } } Connector/Sabre/Node.php 0000604 00000020440 15247164651 0011137 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Jakob Sack <mail@jakobsack.de> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author Klaas Freitag <freitag@owncloud.com> * @author Markus Goetz <markus@woboq.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OC\Files\Mount\MoveableMount; use OC\Files\View; use OCA\DAV\Connector\Sabre\Exception\InvalidPath; use OCP\Files\FileInfo; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; abstract class Node implements \Sabre\DAV\INode { /** * @var \OC\Files\View */ protected $fileView; /** * The path to the current node * * @var string */ protected $path; /** * node properties cache * * @var array */ protected $property_cache = null; /** * @var \OCP\Files\FileInfo */ protected $info; /** * @var IManager */ protected $shareManager; /** * Sets up the node, expects a full path name * * @param \OC\Files\View $view * @param \OCP\Files\FileInfo $info * @param IManager $shareManager */ public function __construct(View $view, FileInfo $info, IManager $shareManager = null) { $this->fileView = $view; $this->path = $this->fileView->getRelativePath($info->getPath()); $this->info = $info; if ($shareManager) { $this->shareManager = $shareManager; } else { $this->shareManager = \OC::$server->getShareManager(); } } protected function refreshInfo() { $this->info = $this->fileView->getFileInfo($this->path); } /** * Returns the name of the node * * @return string */ public function getName() { return $this->info->getName(); } /** * Returns the full path * * @return string */ public function getPath() { return $this->path; } /** * Renames the node * * @param string $name The new name * @throws \Sabre\DAV\Exception\BadRequest * @throws \Sabre\DAV\Exception\Forbidden */ public function setName($name) { // rename is only allowed if the update privilege is granted if (!$this->info->isUpdateable()) { throw new \Sabre\DAV\Exception\Forbidden(); } list($parentPath,) = \Sabre\HTTP\URLUtil::splitPath($this->path); list(, $newName) = \Sabre\HTTP\URLUtil::splitPath($name); // verify path of the target $this->verifyPath(); $newPath = $parentPath . '/' . $newName; $this->fileView->rename($this->path, $newPath); $this->path = $newPath; $this->refreshInfo(); } public function setPropertyCache($property_cache) { $this->property_cache = $property_cache; } /** * Returns the last modification time, as a unix timestamp * * @return int timestamp as integer */ public function getLastModified() { $timestamp = $this->info->getMtime(); if (!empty($timestamp)) { return (int)$timestamp; } return $timestamp; } /** * sets the last modification time of the file (mtime) to the value given * in the second parameter or to now if the second param is empty. * Even if the modification time is set to a custom value the access time is set to now. */ public function touch($mtime) { $this->fileView->touch($this->path, $mtime); $this->refreshInfo(); } /** * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the * file. If the file changes, the ETag MUST change. The ETag is an * arbitrary string, but MUST be surrounded by double-quotes. * * Return null if the ETag can not effectively be determined * * @return string */ public function getETag() { return '"' . $this->info->getEtag() . '"'; } /** * Sets the ETag * * @param string $etag * * @return int file id of updated file or -1 on failure */ public function setETag($etag) { return $this->fileView->putFileInfo($this->path, array('etag' => $etag)); } /** * Returns the size of the node, in bytes * * @return integer */ public function getSize() { return $this->info->getSize(); } /** * Returns the cache's file id * * @return int */ public function getId() { return $this->info->getId(); } /** * @return string|null */ public function getFileId() { if ($this->info->getId()) { $instanceId = \OC_Util::getInstanceId(); $id = sprintf('%08d', $this->info->getId()); return $id . $instanceId; } return null; } /** * @return integer */ public function getInternalFileId() { return $this->info->getId(); } /** * @param string $user * @return int */ public function getSharePermissions($user) { // check of we access a federated share if ($user !== null) { try { $share = $this->shareManager->getShareByToken($user); return $share->getPermissions(); } catch (ShareNotFound $e) { // ignore } } $storage = $this->info->getStorage(); $path = $this->info->getInternalPath(); if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { /** @var \OCA\Files_Sharing\SharedStorage $storage */ $permissions = (int)$storage->getShare()->getPermissions(); } else { $permissions = $storage->getPermissions($path); } /* * We can always share non moveable mount points with DELETE and UPDATE * Eventually we need to do this properly */ $mountpoint = $this->info->getMountPoint(); if (!($mountpoint instanceof MoveableMount)) { $mountpointpath = $mountpoint->getMountPoint(); if (substr($mountpointpath, -1) === '/') { $mountpointpath = substr($mountpointpath, 0, -1); } if ($mountpointpath === $this->info->getPath()) { $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE; } } /* * Files can't have create or delete permissions */ if ($this->info->getType() === \OCP\Files\FileInfo::TYPE_FILE) { $permissions &= ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_DELETE); } return $permissions; } /** * @return string */ public function getDavPermissions() { $p = ''; if ($this->info->isShared()) { $p .= 'S'; } if ($this->info->isShareable()) { $p .= 'R'; } if ($this->info->isMounted()) { $p .= 'M'; } if ($this->info->isDeletable()) { $p .= 'D'; } if ($this->info->isUpdateable()) { $p .= 'NV'; // Renameable, Moveable } if ($this->info->getType() === \OCP\Files\FileInfo::TYPE_FILE) { if ($this->info->isUpdateable()) { $p .= 'W'; } } else { if ($this->info->isCreatable()) { $p .= 'CK'; } } return $p; } public function getOwner() { return $this->info->getOwner(); } protected function verifyPath() { try { $fileName = basename($this->info->getPath()); $this->fileView->verifyPath($this->path, $fileName); } catch (\OCP\Files\InvalidPathException $ex) { throw new InvalidPath($ex->getMessage()); } } /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE */ public function acquireLock($type) { $this->fileView->lockFile($this->path, $type); } /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE */ public function releaseLock($type) { $this->fileView->unlockFile($this->path, $type); } /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE */ public function changeLock($type) { $this->fileView->changeLock($this->path, $type); } public function getFileInfo() { return $this->info; } } Connector/Sabre/BlockLegacyClientPlugin.php 0000604 00000004671 15247164651 0014757 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCP\IConfig; use Sabre\HTTP\RequestInterface; use Sabre\DAV\ServerPlugin; /** * Class BlockLegacyClientPlugin is used to detect old legacy sync clients and * returns a 403 status to those clients * * @package OCA\DAV\Connector\Sabre */ class BlockLegacyClientPlugin extends ServerPlugin { /** @var \Sabre\DAV\Server */ protected $server; /** @var IConfig */ protected $config; /** * @param IConfig $config */ public function __construct(IConfig $config) { $this->config = $config; } /** * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $this->server->on('beforeMethod', [$this, 'beforeHandler'], 200); } /** * Detects all unsupported clients and throws a \Sabre\DAV\Exception\Forbidden * exception which will result in a 403 to them. * @param RequestInterface $request * @throws \Sabre\DAV\Exception\Forbidden If the client version is not supported */ public function beforeHandler(RequestInterface $request) { $userAgent = $request->getHeader('User-Agent'); if($userAgent === null) { return; } $minimumSupportedDesktopVersion = $this->config->getSystemValue('minimum.supported.desktop.version', '2.0.0'); // Match on the mirall version which is in scheme "Mozilla/5.0 (%1) mirall/%2" or // "mirall/%1" for older releases preg_match("/(?:mirall\\/)([\d.]+)/i", $userAgent, $versionMatches); if(isset($versionMatches[1]) && version_compare($versionMatches[1], $minimumSupportedDesktopVersion) === -1) { throw new \Sabre\DAV\Exception\Forbidden('Unsupported client version.'); } } } Connector/Sabre/Directory.php 0000604 00000033715 15247164651 0012227 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bart Visscher <bartv@thisnet.nl> * @author Björn Schießle <bjoern@schiessle.org> * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OC\Files\View; use OCA\DAV\Connector\Sabre\Exception\Forbidden; use OCA\DAV\Connector\Sabre\Exception\InvalidPath; use OCA\DAV\Connector\Sabre\Exception\FileLocked; use OCP\Files\FileInfo; use OCP\Files\ForbiddenException; use OCP\Files\InvalidPathException; use OCP\Files\StorageNotAvailableException; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; use Sabre\DAV\Exception\Locked; use Sabre\DAV\Exception\ServiceUnavailable; use Sabre\DAV\INode; use Sabre\DAV\Exception\BadRequest; use OC\Files\Mount\MoveableMount; use Sabre\DAV\IFile; use Sabre\DAV\Exception\NotFound; class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota, \Sabre\DAV\IMoveTarget { /** * Cached directory content * * @var \OCP\Files\FileInfo[] */ private $dirContent; /** * Cached quota info * * @var array */ private $quotaInfo; /** * @var ObjectTree|null */ private $tree; /** * Sets up the node, expects a full path name * * @param \OC\Files\View $view * @param \OCP\Files\FileInfo $info * @param ObjectTree|null $tree * @param \OCP\Share\IManager $shareManager */ public function __construct(View $view, FileInfo $info, $tree = null, $shareManager = null) { parent::__construct($view, $info, $shareManager); $this->tree = $tree; } /** * Creates a new file in the directory * * Data will either be supplied as a stream resource, or in certain cases * as a string. Keep in mind that you may have to support either. * * After successful creation of the file, you may choose to return the ETag * of the new file here. * * The returned ETag must be surrounded by double-quotes (The quotes should * be part of the actual string). * * If you cannot accurately determine the ETag, you should not return it. * If you don't store the file exactly as-is (you're transforming it * somehow) you should also not return an ETag. * * This means that if a subsequent GET to this new file does not exactly * return the same contents of what was submitted here, you are strongly * recommended to omit the ETag. * * @param string $name Name of the file * @param resource|string $data Initial payload * @return null|string * @throws Exception\EntityTooLarge * @throws Exception\UnsupportedMediaType * @throws FileLocked * @throws InvalidPath * @throws \Sabre\DAV\Exception * @throws \Sabre\DAV\Exception\BadRequest * @throws \Sabre\DAV\Exception\Forbidden * @throws \Sabre\DAV\Exception\ServiceUnavailable */ public function createFile($name, $data = null) { try { // for chunked upload also updating a existing file is a "createFile" // because we create all the chunks before re-assemble them to the existing file. if (isset($_SERVER['HTTP_OC_CHUNKED'])) { // exit if we can't create a new file and we don't updatable existing file $chunkInfo = \OC_FileChunking::decodeName($name); if (!$this->fileView->isCreatable($this->path) && !$this->fileView->isUpdatable($this->path . '/' . $chunkInfo['name']) ) { throw new \Sabre\DAV\Exception\Forbidden(); } } else { // For non-chunked upload it is enough to check if we can create a new file if (!$this->fileView->isCreatable($this->path)) { throw new \Sabre\DAV\Exception\Forbidden(); } } $this->fileView->verifyPath($this->path, $name); $path = $this->fileView->getAbsolutePath($this->path) . '/' . $name; // in case the file already exists/overwriting $info = $this->fileView->getFileInfo($this->path . '/' . $name); if (!$info) { // use a dummy FileInfo which is acceptable here since it will be refreshed after the put is complete $info = new \OC\Files\FileInfo($path, null, null, [], null); } $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info); $node->acquireLock(ILockingProvider::LOCK_SHARED); return $node->put($data); } catch (\OCP\Files\StorageNotAvailableException $e) { throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage()); } catch (InvalidPathException $ex) { throw new InvalidPath($ex->getMessage()); } catch (ForbiddenException $ex) { throw new Forbidden($ex->getMessage(), $ex->getRetry()); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } } /** * Creates a new subdirectory * * @param string $name * @throws FileLocked * @throws InvalidPath * @throws \Sabre\DAV\Exception\Forbidden * @throws \Sabre\DAV\Exception\ServiceUnavailable */ public function createDirectory($name) { try { if (!$this->info->isCreatable()) { throw new \Sabre\DAV\Exception\Forbidden(); } $this->fileView->verifyPath($this->path, $name); $newPath = $this->path . '/' . $name; if (!$this->fileView->mkdir($newPath)) { throw new \Sabre\DAV\Exception\Forbidden('Could not create directory ' . $newPath); } } catch (\OCP\Files\StorageNotAvailableException $e) { throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage()); } catch (InvalidPathException $ex) { throw new InvalidPath($ex->getMessage()); } catch (ForbiddenException $ex) { throw new Forbidden($ex->getMessage(), $ex->getRetry()); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } } /** * Returns a specific child node, referenced by its name * * @param string $name * @param \OCP\Files\FileInfo $info * @return \Sabre\DAV\INode * @throws InvalidPath * @throws \Sabre\DAV\Exception\NotFound * @throws \Sabre\DAV\Exception\ServiceUnavailable */ public function getChild($name, $info = null) { if (!$this->info->isReadable()) { // avoid detecting files through this way throw new NotFound(); } $path = $this->path . '/' . $name; if (is_null($info)) { try { $this->fileView->verifyPath($this->path, $name); $info = $this->fileView->getFileInfo($path); } catch (\OCP\Files\StorageNotAvailableException $e) { throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage()); } catch (InvalidPathException $ex) { throw new InvalidPath($ex->getMessage()); } catch (ForbiddenException $e) { throw new \Sabre\DAV\Exception\Forbidden(); } } if (!$info) { throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located'); } if ($info['mimetype'] == 'httpd/unix-directory') { $node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this->tree, $this->shareManager); } else { $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info, $this->shareManager); } if ($this->tree) { $this->tree->cacheNode($node); } return $node; } /** * Returns an array with all the child nodes * * @return \Sabre\DAV\INode[] * @throws \Sabre\DAV\Exception\Locked * @throws \OCA\DAV\Connector\Sabre\Exception\Forbidden */ public function getChildren() { if (!is_null($this->dirContent)) { return $this->dirContent; } try { if (!$this->info->isReadable()) { // return 403 instead of 404 because a 404 would make // the caller believe that the collection itself does not exist throw new Forbidden('No read permissions'); } $folderContent = $this->fileView->getDirectoryContent($this->path); } catch (LockedException $e) { throw new Locked(); } $nodes = array(); foreach ($folderContent as $info) { $node = $this->getChild($info->getName(), $info); $nodes[] = $node; } $this->dirContent = $nodes; return $this->dirContent; } /** * Checks if a child exists. * * @param string $name * @return bool */ public function childExists($name) { // note: here we do NOT resolve the chunk file name to the real file name // to make sure we return false when checking for file existence with a chunk // file name. // This is to make sure that "createFile" is still triggered // (required old code) instead of "updateFile". // // TODO: resolve chunk file name here and implement "updateFile" $path = $this->path . '/' . $name; return $this->fileView->file_exists($path); } /** * Deletes all files in this directory, and then itself * * @return void * @throws FileLocked * @throws \Sabre\DAV\Exception\Forbidden */ public function delete() { if ($this->path === '' || $this->path === '/' || !$this->info->isDeletable()) { throw new \Sabre\DAV\Exception\Forbidden(); } try { if (!$this->fileView->rmdir($this->path)) { // assume it wasn't possible to remove due to permission issue throw new \Sabre\DAV\Exception\Forbidden(); } } catch (ForbiddenException $ex) { throw new Forbidden($ex->getMessage(), $ex->getRetry()); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } } /** * Returns available diskspace information * * @return array */ public function getQuotaInfo() { if ($this->quotaInfo) { return $this->quotaInfo; } try { $storageInfo = \OC_Helper::getStorageInfo($this->info->getPath(), $this->info); if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) { $free = \OCP\Files\FileInfo::SPACE_UNLIMITED; } else { $free = $storageInfo['free']; } $this->quotaInfo = array( $storageInfo['used'], $free ); return $this->quotaInfo; } catch (\OCP\Files\StorageNotAvailableException $e) { return array(0, 0); } } /** * Moves a node into this collection. * * It is up to the implementors to: * 1. Create the new resource. * 2. Remove the old resource. * 3. Transfer any properties or other data. * * Generally you should make very sure that your collection can easily move * the move. * * If you don't, just return false, which will trigger sabre/dav to handle * the move itself. If you return true from this function, the assumption * is that the move was successful. * * @param string $targetName New local file/collection name. * @param string $fullSourcePath Full path to source node * @param INode $sourceNode Source node itself * @return bool * @throws BadRequest * @throws ServiceUnavailable * @throws Forbidden * @throws FileLocked * @throws \Sabre\DAV\Exception\Forbidden */ public function moveInto($targetName, $fullSourcePath, INode $sourceNode) { if (!$sourceNode instanceof Node) { // it's a file of another kind, like FutureFile if ($sourceNode instanceof IFile) { // fallback to default copy+delete handling return false; } throw new BadRequest('Incompatible node types'); } if (!$this->fileView) { throw new ServiceUnavailable('filesystem not setup'); } $destinationPath = $this->getPath() . '/' . $targetName; $targetNodeExists = $this->childExists($targetName); // at getNodeForPath we also check the path for isForbiddenFileOrDir // with that we have covered both source and destination if ($sourceNode instanceof Directory && $targetNodeExists) { throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists'); } list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($sourceNode->getPath()); $destinationDir = $this->getPath(); $sourcePath = $sourceNode->getPath(); $isMovableMount = false; $sourceMount = \OC::$server->getMountManager()->find($this->fileView->getAbsolutePath($sourcePath)); $internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath)); if ($sourceMount instanceof MoveableMount && $internalPath === '') { $isMovableMount = true; } try { $sameFolder = ($sourceDir === $destinationDir); // if we're overwriting or same folder if ($targetNodeExists || $sameFolder) { // note that renaming a share mount point is always allowed if (!$this->fileView->isUpdatable($destinationDir) && !$isMovableMount) { throw new \Sabre\DAV\Exception\Forbidden(); } } else { if (!$this->fileView->isCreatable($destinationDir)) { throw new \Sabre\DAV\Exception\Forbidden(); } } if (!$sameFolder) { // moving to a different folder, source will be gone, like a deletion // note that moving a share mount point is always allowed if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) { throw new \Sabre\DAV\Exception\Forbidden(); } } $fileName = basename($destinationPath); try { $this->fileView->verifyPath($destinationDir, $fileName); } catch (InvalidPathException $ex) { throw new InvalidPath($ex->getMessage()); } $renameOkay = $this->fileView->rename($sourcePath, $destinationPath); if (!$renameOkay) { throw new \Sabre\DAV\Exception\Forbidden(''); } } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable($e->getMessage()); } catch (ForbiddenException $ex) { throw new Forbidden($ex->getMessage(), $ex->getRetry()); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } return true; } } Connector/Sabre/DavAclPlugin.php 0000604 00000005306 15247164651 0012567 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use Sabre\CalDAV\Principal\User; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\INode; use \Sabre\DAV\PropFind; /** * Class DavAclPlugin is a wrapper around \Sabre\DAVACL\Plugin that returns 404 * responses in case the resource to a response has been forbidden instead of * a 403. This is used to prevent enumeration of valid resources. * * @see https://github.com/owncloud/core/issues/22578 * @package OCA\DAV\Connector\Sabre */ class DavAclPlugin extends \Sabre\DAVACL\Plugin { public function __construct() { $this->hideNodesFromListings = true; $this->allowUnauthenticatedAccess = false; } function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) { $access = parent::checkPrivileges($uri, $privileges, $recursion, false); if($access === false && $throwExceptions) { /** @var INode $node */ $node = $this->server->tree->getNodeForPath($uri); switch(get_class($node)) { case 'OCA\DAV\CardDAV\AddressBook': $type = 'Addressbook'; break; default: $type = 'Node'; break; } throw new NotFound( sprintf( "%s with name '%s' could not be found", $type, $node->getName() ) ); } return $access; } public function propFind(PropFind $propFind, INode $node) { // If the node is neither readable nor writable then fail unless its of // the standard user-principal if(!($node instanceof User)) { $path = $propFind->getPath(); $readPermissions = $this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false); $writePermissions = $this->checkPrivileges($path, '{DAV:}write', self::R_PARENT, false); if ($readPermissions === false && $writePermissions === false) { $this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, true); $this->checkPrivileges($path, '{DAV:}write', self::R_PARENT, true); } } return parent::propFind($propFind, $node); } } Connector/Sabre/CustomPropertiesBackend.php 0000604 00000021710 15247164651 0015052 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Aaron Wood <aaronjwood@gmail.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCP\IDBConnection; use OCP\IUser; use Sabre\DAV\PropertyStorage\Backend\BackendInterface; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; use Sabre\DAV\Tree; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Exception\ServiceUnavailable; class CustomPropertiesBackend implements BackendInterface { /** * Ignored properties * * @var array */ private $ignoredProperties = array( '{DAV:}getcontentlength', '{DAV:}getcontenttype', '{DAV:}getetag', '{DAV:}quota-used-bytes', '{DAV:}quota-available-bytes', '{DAV:}quota-available-bytes', '{http://owncloud.org/ns}permissions', '{http://owncloud.org/ns}downloadURL', '{http://owncloud.org/ns}dDC', '{http://owncloud.org/ns}size', ); /** * @var Tree */ private $tree; /** * @var IDBConnection */ private $connection; /** * @var IUser */ private $user; /** * Properties cache * * @var array */ private $cache = []; /** * @param Tree $tree node tree * @param IDBConnection $connection database connection * @param IUser $user owner of the tree and properties */ public function __construct( Tree $tree, IDBConnection $connection, IUser $user) { $this->tree = $tree; $this->connection = $connection; $this->user = $user->getUID(); } /** * Fetches properties for a path. * * @param string $path * @param PropFind $propFind * @return void */ public function propFind($path, PropFind $propFind) { try { $node = $this->tree->getNodeForPath($path); if (!($node instanceof Node)) { return; } } catch (ServiceUnavailable $e) { // might happen for unavailable mount points, skip return; } catch (NotFound $e) { // in some rare (buggy) cases the node might not be found, // we catch the exception to prevent breaking the whole list with a 404 // (soft fail) \OC::$server->getLogger()->warning( 'Could not get node for path: \"' . $path . '\" : ' . $e->getMessage(), array('app' => 'files') ); return; } $requestedProps = $propFind->get404Properties(); // these might appear $requestedProps = array_diff( $requestedProps, $this->ignoredProperties ); if (empty($requestedProps)) { return; } if ($node instanceof Directory && $propFind->getDepth() !== 0 ) { // note: pre-fetching only supported for depth <= 1 $this->loadChildrenProperties($node, $requestedProps); } $props = $this->getProperties($node, $requestedProps); foreach ($props as $propName => $propValue) { $propFind->set($propName, $propValue); } } /** * Updates properties for a path * * @param string $path * @param PropPatch $propPatch * * @return void */ public function propPatch($path, PropPatch $propPatch) { $node = $this->tree->getNodeForPath($path); if (!($node instanceof Node)) { return; } $propPatch->handleRemaining(function($changedProps) use ($node) { return $this->updateProperties($node, $changedProps); }); } /** * This method is called after a node is deleted. * * @param string $path path of node for which to delete properties */ public function delete($path) { $statement = $this->connection->prepare( 'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?' ); $statement->execute(array($this->user, '/' . $path)); $statement->closeCursor(); unset($this->cache[$path]); } /** * This method is called after a successful MOVE * * @param string $source * @param string $destination * * @return void */ public function move($source, $destination) { $statement = $this->connection->prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?' . ' WHERE `userid` = ? AND `propertypath` = ?' ); $statement->execute(array('/' . $destination, $this->user, '/' . $source)); $statement->closeCursor(); } /** * Returns a list of properties for this nodes.; * @param Node $node * @param array $requestedProperties requested properties or empty array for "all" * @return array * @note The properties list is a list of propertynames the client * requested, encoded as xmlnamespace#tagName, for example: * http://www.example.org/namespace#author If the array is empty, all * properties should be returned */ private function getProperties(Node $node, array $requestedProperties) { $path = $node->getPath(); if (isset($this->cache[$path])) { return $this->cache[$path]; } // TODO: chunking if more than 1000 properties $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'; $whereValues = array($this->user, $path); $whereTypes = array(null, null); if (!empty($requestedProperties)) { // request only a subset $sql .= ' AND `propertyname` in (?)'; $whereValues[] = $requestedProperties; $whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY; } $result = $this->connection->executeQuery( $sql, $whereValues, $whereTypes ); $props = []; while ($row = $result->fetch()) { $props[$row['propertyname']] = $row['propertyvalue']; } $result->closeCursor(); $this->cache[$path] = $props; return $props; } /** * Update properties * * @param Node $node node for which to update properties * @param array $properties array of properties to update * * @return bool */ private function updateProperties($node, $properties) { $path = $node->getPath(); $deleteStatement = 'DELETE FROM `*PREFIX*properties`' . ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'; $insertStatement = 'INSERT INTO `*PREFIX*properties`' . ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)'; $updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' . ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'; // TODO: use "insert or update" strategy ? $existing = $this->getProperties($node, array()); $this->connection->beginTransaction(); foreach ($properties as $propertyName => $propertyValue) { // If it was null, we need to delete the property if (is_null($propertyValue)) { if (array_key_exists($propertyName, $existing)) { $this->connection->executeUpdate($deleteStatement, array( $this->user, $path, $propertyName ) ); } } else { if (!array_key_exists($propertyName, $existing)) { $this->connection->executeUpdate($insertStatement, array( $this->user, $path, $propertyName, $propertyValue ) ); } else { $this->connection->executeUpdate($updateStatement, array( $propertyValue, $this->user, $path, $propertyName ) ); } } } $this->connection->commit(); unset($this->cache[$path]); return true; } /** * Bulk load properties for directory children * * @param Directory $node * @param array $requestedProperties requested properties * * @return void */ private function loadChildrenProperties(Directory $node, $requestedProperties) { $path = $node->getPath(); if (isset($this->cache[$path])) { // we already loaded them at some point return; } $childNodes = $node->getChildren(); // pre-fill cache foreach ($childNodes as $childNode) { $this->cache[$childNode->getPath()] = []; } $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` LIKE ?'; $sql .= ' AND `propertyname` in (?) ORDER BY `propertypath`, `propertyname`'; $result = $this->connection->executeQuery( $sql, array($this->user, $this->connection->escapeLikeParameter(rtrim($path, '/')) . '/%', $requestedProperties), array(null, null, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY) ); $oldPath = null; $props = []; while ($row = $result->fetch()) { $path = $row['propertypath']; if ($oldPath !== $path) { // save previously gathered props $this->cache[$oldPath] = $props; $oldPath = $path; // prepare props for next path $props = []; } $props[$row['propertyname']] = $row['propertyvalue']; } if (!is_null($oldPath)) { // save props from last run $this->cache[$oldPath] = $props; } $result->closeCursor(); } } Connector/Sabre/FakeLockerPlugin.php 0000604 00000010165 15247164651 0013442 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use Sabre\DAV\Locks\LockInfo; use Sabre\DAV\ServerPlugin; use Sabre\DAV\Xml\Property\LockDiscovery; use Sabre\DAV\Xml\Property\SupportedLock; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; use Sabre\DAV\PropFind; use Sabre\DAV\INode; /** * Class FakeLockerPlugin is a plugin only used when connections come in from * OS X via Finder. The fake locking plugin does emulate Class 2 WebDAV support * (locking of files) which allows Finder to access the storage in write mode as * well. * * No real locking is performed, instead the plugin just returns always positive * responses. * * @see https://github.com/owncloud/core/issues/17732 * @package OCA\DAV\Connector\Sabre */ class FakeLockerPlugin extends ServerPlugin { /** @var \Sabre\DAV\Server */ private $server; /** {@inheritDoc} */ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $this->server->on('method:LOCK', [$this, 'fakeLockProvider'], 1); $this->server->on('method:UNLOCK', [$this, 'fakeUnlockProvider'], 1); $server->on('propFind', [$this, 'propFind']); $server->on('validateTokens', [$this, 'validateTokens']); } /** * Indicate that we support LOCK and UNLOCK * * @param string $path * @return string[] */ public function getHTTPMethods($path) { return [ 'LOCK', 'UNLOCK', ]; } /** * Indicate that we support locking * * @return integer[] */ function getFeatures() { return [2]; } /** * Return some dummy response for PROPFIND requests with regard to locking * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { $propFind->handle('{DAV:}supportedlock', function() { return new SupportedLock(true); }); $propFind->handle('{DAV:}lockdiscovery', function() use ($propFind) { return new LockDiscovery([]); }); } /** * Mark a locking token always as valid * * @param RequestInterface $request * @param array $conditions */ public function validateTokens(RequestInterface $request, &$conditions) { foreach($conditions as &$fileCondition) { if(isset($fileCondition['tokens'])) { foreach($fileCondition['tokens'] as &$token) { if(isset($token['token'])) { if(substr($token['token'], 0, 16) === 'opaquelocktoken:') { $token['validToken'] = true; } } } } } } /** * Fakes a successful LOCK * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ public function fakeLockProvider(RequestInterface $request, ResponseInterface $response) { $lockInfo = new LockInfo(); $lockInfo->token = md5($request->getPath()); $lockInfo->uri = $request->getPath(); $lockInfo->depth = \Sabre\DAV\Server::DEPTH_INFINITY; $lockInfo->timeout = 1800; $body = $this->server->xml->write('{DAV:}prop', [ '{DAV:}lockdiscovery' => new LockDiscovery([$lockInfo]) ]); $response->setStatus(200); $response->setBody($body); return false; } /** * Fakes a successful LOCK * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ public function fakeUnlockProvider(RequestInterface $request, ResponseInterface $response) { $response->setStatus(204); $response->setHeader('Content-Length', '0'); return false; } } Connector/Sabre/AppEnabledPlugin.php 0000604 00000004322 15247164651 0013425 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector\Sabre; use OCP\App\IAppManager; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\ServerPlugin; /** * Plugin to check if an app is enabled for the current user */ class AppEnabledPlugin extends ServerPlugin { /** * Reference to main server object * * @var \Sabre\DAV\Server */ private $server; /** * @var string */ private $app; /** * @var \OCP\App\IAppManager */ private $appManager; /** * @param string $app * @param \OCP\App\IAppManager $appManager */ public function __construct($app, IAppManager $appManager) { $this->app = $app; $this->appManager = $appManager; } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $this->server->on('beforeMethod', array($this, 'checkAppEnabled'), 30); } /** * This method is called before any HTTP after auth and checks if the user has access to the app * * @throws \Sabre\DAV\Exception\Forbidden * @return bool */ public function checkAppEnabled() { if (!$this->appManager->isEnabledForUser($this->app)) { throw new Forbidden(); } } } Connector/PublicAuth.php 0000604 00000007051 15247164651 0011261 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector; use OCP\IRequest; use OCP\ISession; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; use Sabre\DAV\Auth\Backend\AbstractBasic; /** * Class PublicAuth * * @package OCA\DAV\Connector */ class PublicAuth extends AbstractBasic { /** @var \OCP\Share\IShare */ private $share; /** @var IManager */ private $shareManager; /** @var ISession */ private $session; /** @var IRequest */ private $request; /** * @param IRequest $request * @param IManager $shareManager * @param ISession $session */ public function __construct(IRequest $request, IManager $shareManager, ISession $session) { $this->request = $request; $this->shareManager = $shareManager; $this->session = $session; // setup realm $defaults = new \OCP\Defaults(); $this->realm = $defaults->getName(); } /** * Validates a username and password * * This method should return true or false depending on if login * succeeded. * * @param string $username * @param string $password * * @return bool * @throws \Sabre\DAV\Exception\NotAuthenticated */ protected function validateUserPass($username, $password) { try { $share = $this->shareManager->getShareByToken($username); } catch (ShareNotFound $e) { return false; } $this->share = $share; \OC_User::setIncognitoMode(true); // check if the share is password protected if ($share->getPassword() !== null) { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { if ($this->shareManager->checkPassword($share, $password)) { return true; } else if ($this->session->exists('public_link_authenticated') && $this->session->get('public_link_authenticated') === (string)$share->getId()) { return true; } else { if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) { // do not re-authenticate over ajax, use dummy auth name to prevent browser popup http_response_code(401); header('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"'); throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls'); } return false; } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { return true; } else { return false; } } else { return true; } } /** * @return \OCP\Share\IShare */ public function getShare() { return $this->share; } } Connector/LegacyDAVACL.php 0000604 00000003753 15247164651 0011305 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Connector; use OCA\DAV\Connector\Sabre\DavAclPlugin; use Sabre\DAV\INode; use Sabre\DAV\PropFind; use Sabre\HTTP\URLUtil; use Sabre\DAVACL\Xml\Property\Principal; class LegacyDAVACL extends DavAclPlugin { /** * @inheritdoc */ public function getCurrentUserPrincipals() { $principalV2 = $this->getCurrentUserPrincipal(); if (is_null($principalV2)) return []; $principalV1 = $this->convertPrincipal($principalV2, false); return array_merge( [ $principalV2, $principalV1 ], $this->getPrincipalMembership($principalV1) ); } private function convertPrincipal($principal, $toV2) { list(, $name) = URLUtil::splitPath($principal); if ($toV2) { return "principals/users/$name"; } return "principals/$name"; } public function propFind(PropFind $propFind, INode $node) { /* Overload current-user-principal */ $propFind->handle('{DAV:}current-user-principal', function () { if ($url = parent::getCurrentUserPrincipal()) { return new Principal(Principal::HREF, $url . '/'); } else { return new Principal(Principal::UNAUTHENTICATED); } }); return parent::propFind($propFind, $node); } } Migration/BuildCalendarSearchIndex.php 0000604 00000004424 15247164651 0014030 0 ustar 00 <?php /** * @copyright 2017 Georg Ehrke <oc.list@georgehrke.com> * * @author Georg Ehrke <oc.list@georgehrke.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\Migration; use OCP\BackgroundJob\IJobList; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class BuildCalendarSearchIndex implements IRepairStep { /** @var IDBConnection */ private $db; /** @var IJobList */ private $jobList; /** @var IConfig */ private $config; /** * @param IDBConnection $db * @param IJobList $jobList * @param IConfig $config */ public function __construct(IDBConnection $db, IJobList $jobList, IConfig $config) { $this->db = $db; $this->jobList = $jobList; $this->config = $config; } /** * @return string */ public function getName() { return 'Registering building of calendar search index as background job'; } /** * @param IOutput $output */ public function run(IOutput $output) { // only run once if ($this->config->getAppValue('dav', 'buildCalendarSearchIndex') === 'yes') { $output->info('Repair step already executed'); return; } $query = $this->db->getQueryBuilder(); $query->select($query->createFunction('MAX(id)')) ->from('calendarobjects'); $maxId = (int)$query->execute()->fetchColumn(); $output->info('Add background job'); $this->jobList->add(BuildCalendarSearchIndexBackgroundJob::class, [ 'offset' => 0, 'stopAt' => $maxId ]); // if all were done, no need to redo the repair during next upgrade $this->config->setAppValue('dav', 'buildCalendarSearchIndex', 'yes'); } } Migration/FixBirthdayCalendarComponent.php 0000604 00000003272 15247164651 0014753 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2016, ownCloud GmbH. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Migration; use OCA\DAV\CalDAV\BirthdayService; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class FixBirthdayCalendarComponent implements IRepairStep { /** @var IDBConnection */ private $connection; /** * FixBirthdayCalendarComponent constructor. * * @param IDBConnection $connection */ public function __construct(IDBConnection $connection) { $this->connection = $connection; } /** * @inheritdoc */ public function getName() { return 'Fix component of birthday calendars'; } /** * @inheritdoc */ public function run(IOutput $output) { $query = $this->connection->getQueryBuilder(); $updated = $query->update('calendars') ->set('components', $query->createNamedParameter('VEVENT')) ->where($query->expr()->eq('uri', $query->createNamedParameter(BirthdayService::BIRTHDAY_CALENDAR_URI))) ->execute(); $output->info("$updated birthday calendars updated."); } } Migration/CalDAVRemoveEmptyValue.php 0000604 00000006366 15247164651 0013454 0 ustar 00 <?php /** * @copyright 2017 Joas Schilling <coding@schilljs.com> * * @author Joas Schilling <coding@schilljs.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\Migration; use OCA\DAV\CalDAV\CalDavBackend; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\ILogger; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; use Sabre\VObject\InvalidDataException; class CalDAVRemoveEmptyValue implements IRepairStep { /** @var IDBConnection */ private $db; /** @var CalDavBackend */ private $calDavBackend; /** @var ILogger */ private $logger; /** * @param IDBConnection $db * @param CalDavBackend $calDavBackend * @param ILogger $logger */ public function __construct(IDBConnection $db, CalDavBackend $calDavBackend, ILogger $logger) { $this->db = $db; $this->calDavBackend = $calDavBackend; $this->logger = $logger; } public function getName() { return 'Fix broken values of calendar objects'; } public function run(IOutput $output) { $pattern = ';VALUE=:'; $count = $warnings = 0; $objects = $this->getInvalidObjects($pattern); $output->startProgress(count($objects)); foreach ($objects as $row) { $calObject = $this->calDavBackend->getCalendarObject((int)$row['calendarid'], $row['uri']); $data = preg_replace('/' . $pattern . '/', ':', $calObject['calendardata']); if ($data !== $calObject['calendardata']) { $output->advance(); try { $this->calDavBackend->getDenormalizedData($data); } catch (InvalidDataException $e) { $this->logger->info('Calendar object for calendar {cal} with uri {uri} still invalid', [ 'app' => 'dav', 'cal' => (int)$row['calendarid'], 'uri' => $row['uri'], ]); $warnings++; continue; } $this->calDavBackend->updateCalendarObject((int)$row['calendarid'], $row['uri'], $data); $count++; } } $output->finishProgress(); if ($warnings > 0) { $output->warning(sprintf('%d events could not be updated, see log file for more information', $warnings)); } if ($count > 0) { $output->info(sprintf('Updated %d events', $count)); } } protected function getInvalidObjects($pattern) { $query = $this->db->getQueryBuilder(); $query->select(['calendarid', 'uri']) ->from('calendarobjects') ->where($query->expr()->like( 'calendardata', $query->createNamedParameter( '%' . $this->db->escapeLikeParameter($pattern) . '%', IQueryBuilder::PARAM_STR ), IQueryBuilder::PARAM_STR )); $result = $query->execute(); $rows = $result->fetchAll(); $result->closeCursor(); return $rows; } } Migration/BuildCalendarSearchIndexBackgroundJob.php 0000604 00000006366 15247164651 0016472 0 ustar 00 <?php /** * @copyright 2017 Georg Ehrke <oc.list@georgehrke.com> * * @author Georg Ehrke <oc.list@georgehrke.com> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\Migration; use OC\BackgroundJob\QueuedJob; use OCA\DAV\CalDAV\CalDavBackend; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; use OCP\IDBConnection; use OCP\ILogger; class BuildCalendarSearchIndexBackgroundJob extends QueuedJob { /** @var IDBConnection */ private $db; /** @var CalDavBackend */ private $calDavBackend; /** @var ILogger */ private $logger; /** @var IJobList */ private $jobList; /** @var ITimeFactory */ private $timeFactory; /** * @param IDBConnection $db * @param CalDavBackend $calDavBackend * @param ILogger $logger * @param IJobList $jobList * @param ITimeFactory $timeFactory */ public function __construct(IDBConnection $db, CalDavBackend $calDavBackend, ILogger $logger, IJobList $jobList, ITimeFactory $timeFactory) { $this->db = $db; $this->calDavBackend = $calDavBackend; $this->logger = $logger; $this->jobList = $jobList; $this->timeFactory = $timeFactory; } public function run($arguments) { $offset = $arguments['offset']; $stopAt = $arguments['stopAt']; $this->logger->info('Building calendar index (' . $offset .'/' . $stopAt . ')'); $offset = $this->buildIndex($offset, $stopAt); if ($offset >= $stopAt) { $this->logger->info('Building calendar index done'); } else { $this->jobList->add(self::class, [ 'offset' => $offset, 'stopAt' => $stopAt ]); $this->logger->info('New building calendar index job scheduled with offset ' . $offset); } } /** * @param int $offset * @param int $stopAt * @return int */ private function buildIndex($offset, $stopAt) { $startTime = $this->timeFactory->getTime(); $query = $this->db->getQueryBuilder(); $query->select(['id', 'calendarid', 'uri', 'calendardata']) ->from('calendarobjects') ->where($query->expr()->lte('id', $query->createNamedParameter($stopAt))) ->andWhere($query->expr()->gt('id', $query->createNamedParameter($offset))) ->orderBy('id', 'ASC'); $stmt = $query->execute(); while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $offset = $row['id']; $calendarData = $row['calendardata']; if (is_resource($calendarData)) { $calendarData = stream_get_contents($calendarData); } $this->calDavBackend->updateProperties($row['calendarid'], $row['uri'], $calendarData); if (($this->timeFactory->getTime() - $startTime) > 15) { return $offset; } } return $stopAt; } } DAV/PublicAuth.php 0000604 00000004703 15247164651 0007742 0 ustar 00 <?php /** * @author Thomas Müller <thomas.mueller@tmit.eu> * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV; use Sabre\DAV\Auth\Backend\BackendInterface; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; class PublicAuth implements BackendInterface { /** @var string[] */ private $publicURLs; public function __construct() { $this->publicURLs = [ 'public-calendars', 'principals/system/public' ]; } /** * When this method is called, the backend must check if authentication was * successful. * * The returned value must be one of the following * * [true, "principals/username"] * [false, "reason for failure"] * * If authentication was successful, it's expected that the authentication * backend returns a so-called principal url. * * Examples of a principal url: * * principals/admin * principals/user1 * principals/users/joe * principals/uid/123457 * * If you don't use WebDAV ACL (RFC3744) we recommend that you simply * return a string such as: * * principals/users/[username] * * @param RequestInterface $request * @param ResponseInterface $response * @return array */ function check(RequestInterface $request, ResponseInterface $response) { if ($this->isRequestPublic($request)) { return [true, "principals/system/public"]; } return [false, "No public access to this resource."]; } /** * @inheritdoc */ function challenge(RequestInterface $request, ResponseInterface $response) { } /** * @param RequestInterface $request * @return bool */ private function isRequestPublic(RequestInterface $request) { $url = $request->getPath(); $matchingUrls = array_filter($this->publicURLs, function ($publicUrl) use ($url) { return strpos($url, $publicUrl, 0) === 0; }); return !empty($matchingUrls); } } DAV/Sharing/Xml/Invite.php 0000604 00000011517 15247164651 0011274 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV\Sharing\Xml; use OCA\DAV\DAV\Sharing\Plugin; use Sabre\Xml\Writer; use Sabre\Xml\XmlSerializable; /** * Invite property * * This property encodes the 'invite' property, as defined by * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/ * namespace. * * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class Invite implements XmlSerializable { /** * The list of users a calendar has been shared to. * * @var array */ protected $users; /** * The organizer contains information about the person who shared the * object. * * @var array|null */ protected $organizer; /** * Creates the property. * * Users is an array. Each element of the array has the following * properties: * * * href - Often a mailto: address * * commonName - Optional, for example a first and lastname for a user. * * status - One of the SharingPlugin::STATUS_* constants. * * readOnly - true or false * * summary - Optional, description of the share * * The organizer key is optional to specify. It's only useful when a * 'sharee' requests the sharing information. * * The organizer may have the following properties: * * href - Often a mailto: address. * * commonName - Optional human-readable name. * * firstName - Optional first name. * * lastName - Optional last name. * * If you wonder why these two structures are so different, I guess a * valid answer is that the current spec is still a draft. * * @param array $users */ function __construct(array $users, array $organizer = null) { $this->users = $users; $this->organizer = $organizer; } /** * Returns the list of users, as it was passed to the constructor. * * @return array */ function getValue() { return $this->users; } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Writer $writer) { $cs = '{' . Plugin::NS_OWNCLOUD . '}'; if (!is_null($this->organizer)) { $writer->startElement($cs . 'organizer'); $writer->writeElement('{DAV:}href', $this->organizer['href']); if (isset($this->organizer['commonName']) && $this->organizer['commonName']) { $writer->writeElement($cs . 'common-name', $this->organizer['commonName']); } if (isset($this->organizer['firstName']) && $this->organizer['firstName']) { $writer->writeElement($cs . 'first-name', $this->organizer['firstName']); } if (isset($this->organizer['lastName']) && $this->organizer['lastName']) { $writer->writeElement($cs . 'last-name', $this->organizer['lastName']); } $writer->endElement(); // organizer } foreach ($this->users as $user) { $writer->startElement($cs . 'user'); $writer->writeElement('{DAV:}href', $user['href']); if (isset($user['commonName']) && $user['commonName']) { $writer->writeElement($cs . 'common-name', $user['commonName']); } $writer->writeElement($cs . 'invite-accepted'); $writer->startElement($cs . 'access'); if ($user['readOnly']) { $writer->writeElement($cs . 'read'); } else { $writer->writeElement($cs . 'read-write'); } $writer->endElement(); // access if (isset($user['summary']) && $user['summary']) { $writer->writeElement($cs . 'summary', $user['summary']); } $writer->endElement(); //user } } } DAV/Sharing/Xml/ShareRequest.php 0000604 00000004744 15247164651 0012455 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV\Sharing\Xml; use OCA\DAV\DAV\Sharing\Plugin; use Sabre\Xml\Reader; use Sabre\Xml\XmlDeserializable; class ShareRequest implements XmlDeserializable { public $set = []; public $remove = []; /** * Constructor * * @param array $set * @param array $remove */ function __construct(array $set, array $remove) { $this->set = $set; $this->remove = $remove; } static function xmlDeserialize(Reader $reader) { $elements = $reader->parseInnerTree([ '{' . Plugin::NS_OWNCLOUD. '}set' => 'Sabre\\Xml\\Element\\KeyValue', '{' . Plugin::NS_OWNCLOUD . '}remove' => 'Sabre\\Xml\\Element\\KeyValue', ]); $set = []; $remove = []; foreach ($elements as $elem) { switch ($elem['name']) { case '{' . Plugin::NS_OWNCLOUD . '}set' : $sharee = $elem['value']; $sumElem = '{' . Plugin::NS_OWNCLOUD . '}summary'; $commonName = '{' . Plugin::NS_OWNCLOUD . '}common-name'; $set[] = [ 'href' => $sharee['{DAV:}href'], 'commonName' => isset($sharee[$commonName]) ? $sharee[$commonName] : null, 'summary' => isset($sharee[$sumElem]) ? $sharee[$sumElem] : null, 'readOnly' => !array_key_exists('{' . Plugin::NS_OWNCLOUD . '}read-write', $sharee), ]; break; case '{' . Plugin::NS_OWNCLOUD . '}remove' : $remove[] = $elem['value']['{DAV:}href']; break; } } return new self($set, $remove); } } DAV/Sharing/Backend.php 0000604 00000014516 15247164651 0010627 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV\Sharing; use OCA\DAV\Connector\Sabre\Principal; use OCP\IDBConnection; class Backend { /** @var IDBConnection */ private $db; /** @var Principal */ private $principalBackend; /** @var string */ private $resourceType; const ACCESS_OWNER = 1; const ACCESS_READ_WRITE = 2; const ACCESS_READ = 3; /** * @param IDBConnection $db * @param Principal $principalBackend * @param string $resourceType */ public function __construct(IDBConnection $db, Principal $principalBackend, $resourceType) { $this->db = $db; $this->principalBackend = $principalBackend; $this->resourceType = $resourceType; } /** * @param IShareable $shareable * @param string[] $add * @param string[] $remove */ public function updateShares($shareable, $add, $remove) { foreach($add as $element) { $this->shareWith($shareable, $element); } foreach($remove as $element) { $this->unshare($shareable, $element); } } /** * @param IShareable $shareable * @param string $element */ private function shareWith($shareable, $element) { $user = $element['href']; $parts = explode(':', $user, 2); if ($parts[0] !== 'principal') { return; } // don't share with owner if ($shareable->getOwner() === $parts[1]) { return; } // remove the share if it already exists $this->unshare($shareable, $element['href']); $access = self::ACCESS_READ; if (isset($element['readOnly'])) { $access = $element['readOnly'] ? self::ACCESS_READ : self::ACCESS_READ_WRITE; } $query = $this->db->getQueryBuilder(); $query->insert('dav_shares') ->values([ 'principaluri' => $query->createNamedParameter($parts[1]), 'type' => $query->createNamedParameter($this->resourceType), 'access' => $query->createNamedParameter($access), 'resourceid' => $query->createNamedParameter($shareable->getResourceId()) ]); $query->execute(); } /** * @param $resourceId */ public function deleteAllShares($resourceId) { $query = $this->db->getQueryBuilder(); $query->delete('dav_shares') ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId))) ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType))) ->execute(); } public function deleteAllSharesByUser($principaluri) { $query = $this->db->getQueryBuilder(); $query->delete('dav_shares') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principaluri))) ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType))) ->execute(); } /** * @param IShareable $shareable * @param string $element */ private function unshare($shareable, $element) { $parts = explode(':', $element, 2); if ($parts[0] !== 'principal') { return; } // don't share with owner if ($shareable->getOwner() === $parts[1]) { return; } $query = $this->db->getQueryBuilder(); $query->delete('dav_shares') ->where($query->expr()->eq('resourceid', $query->createNamedParameter($shareable->getResourceId()))) ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType))) ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($parts[1]))) ; $query->execute(); } /** * Returns the list of people whom this resource is shared with. * * Every element in this array should have the following properties: * * href - Often a mailto: address * * commonName - Optional, for example a first + last name * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. * * readOnly - boolean * * summary - Optional, a description for the share * * @param int $resourceId * @return array */ public function getShares($resourceId) { $query = $this->db->getQueryBuilder(); $result = $query->select(['principaluri', 'access']) ->from('dav_shares') ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId))) ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType))) ->execute(); $shares = []; while($row = $result->fetch()) { $p = $this->principalBackend->getPrincipalByPath($row['principaluri']); $shares[]= [ 'href' => "principal:${row['principaluri']}", 'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '', 'status' => 1, 'readOnly' => ($row['access'] == self::ACCESS_READ), '{http://owncloud.org/ns}principal' => $row['principaluri'], '{http://owncloud.org/ns}group-share' => is_null($p) ]; } return $shares; } /** * For shared resources the sharee is set in the ACL of the resource * * @param int $resourceId * @param array $acl * @return array */ public function applyShareAcl($resourceId, $acl) { $shares = $this->getShares($resourceId); foreach ($shares as $share) { $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], 'protected' => true, ]; if (!$share['readOnly']) { $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], 'protected' => true, ]; } else if ($this->resourceType === 'calendar') { // Allow changing the properties of read only calendars, // so users can change the visibility. $acl[] = [ 'privilege' => '{DAV:}write-properties', 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], 'protected' => true, ]; } } return $acl; } } DAV/Sharing/IShareable.php 0000604 00000004076 15247164651 0011277 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV\Sharing; use Sabre\DAV\INode; /** * This interface represents a dav resource that can be shared with other users. * */ interface IShareable extends INode { /** * Updates the list of shares. * * The first array is a list of people that are to be added to the * resource. * * Every element in the add array has the following properties: * * href - A url. Usually a mailto: address * * commonName - Usually a first and last name, or false * * summary - A description of the share, can also be false * * readOnly - A boolean value * * Every element in the remove array is just the address string. * * @param array $add * @param array $remove * @return void */ function updateShares(array $add, array $remove); /** * Returns the list of people whom this resource is shared with. * * Every element in this array should have the following properties: * * href - Often a mailto: address * * commonName - Optional, for example a first + last name * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. * * readOnly - boolean * * summary - Optional, a description for the share * * @return array */ function getShares(); /** * @return int */ public function getResourceId(); /** * @return string */ public function getOwner(); } DAV/Sharing/Plugin.php 0000604 00000012313 15247164651 0010527 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV\Sharing; use OCA\DAV\Connector\Sabre\Auth; use OCA\DAV\DAV\Sharing\Xml\Invite; use OCP\IRequest; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\INode; use Sabre\DAV\PropFind; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; class Plugin extends ServerPlugin { const NS_OWNCLOUD = 'http://owncloud.org/ns'; const NS_NEXTCLOUD = 'http://nextcloud.com/ns'; /** @var Auth */ private $auth; /** @var IRequest */ private $request; /** * Plugin constructor. * * @param Auth $authBackEnd * @param IRequest $request */ public function __construct(Auth $authBackEnd, IRequest $request) { $this->auth = $authBackEnd; $this->request = $request; } /** * Reference to SabreDAV server object. * * @var \Sabre\DAV\Server */ protected $server; /** * This method should return a list of server-features. * * This is for example 'versioning' and is added to the DAV: header * in an OPTIONS response. * * @return string[] */ function getFeatures() { return ['oc-resource-sharing']; } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using Sabre\DAV\Server::getPlugin * * @return string */ function getPluginName() { return 'oc-resource-sharing'; } /** * This initializes the plugin. * * This function is called by Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param Server $server * @return void */ function initialize(Server $server) { $this->server = $server; $this->server->xml->elementMap['{' . Plugin::NS_OWNCLOUD . '}share'] = 'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest'; $this->server->xml->elementMap['{' . Plugin::NS_OWNCLOUD . '}invite'] = 'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite'; $this->server->on('method:POST', [$this, 'httpPost']); $this->server->on('propFind', [$this, 'propFind']); } /** * We intercept this to handle POST requests on a dav resource. * * @param RequestInterface $request * @param ResponseInterface $response * @return null|false */ function httpPost(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Only handling xml $contentType = $request->getHeader('Content-Type'); if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false) return; // Making sure the node exists try { $node = $this->server->tree->getNodeForPath($path); } catch (NotFound $e) { return; } $requestBody = $request->getBodyAsString(); // If this request handler could not deal with this POST request, it // will return 'null' and other plugins get a chance to handle the // request. // // However, we already requested the full body. This is a problem, // because a body can only be read once. This is why we preemptively // re-populated the request body with the existing data. $request->setBody($requestBody); $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType); switch ($documentType) { // Dealing with the 'share' document, which modified invitees on a // calendar. case '{' . self::NS_OWNCLOUD . '}share' : // We can only deal with IShareableCalendar objects if (!$node instanceof IShareable) { return; } $this->server->transactionType = 'post-oc-resource-share'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { /** @var \Sabre\DAVACL\Plugin $acl */ $acl->checkPrivileges($path, '{DAV:}write'); } $node->updateShares($message->set, $message->remove); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; } } /** * This event is triggered when properties are requested for a certain * node. * * This allows us to inject any properties early. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { if ($node instanceof IShareable) { $propFind->handle('{' . Plugin::NS_OWNCLOUD . '}invite', function() use ($node) { return new Invite( $node->getShares() ); }); } } } DAV/GroupPrincipalBackend.php 0000604 00000011131 15247164651 0012101 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV; use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use Sabre\DAV\Exception; use \Sabre\DAV\PropPatch; use Sabre\DAVACL\PrincipalBackend\BackendInterface; class GroupPrincipalBackend implements BackendInterface { const PRINCIPAL_PREFIX = 'principals/groups'; /** @var IGroupManager */ private $groupManager; /** * @param IGroupManager $IGroupManager */ public function __construct(IGroupManager $IGroupManager) { $this->groupManager = $IGroupManager; } /** * Returns a list of principals based on a prefix. * * This prefix will often contain something like 'principals'. You are only * expected to return principals that are in this base path. * * You are expected to return at least a 'uri' for every user, you can * return any additional properties if you wish so. Common properties are: * {DAV:}displayname * * @param string $prefixPath * @return string[] */ public function getPrincipalsByPrefix($prefixPath) { $principals = []; if ($prefixPath === self::PRINCIPAL_PREFIX) { foreach($this->groupManager->search('') as $user) { $principals[] = $this->groupToPrincipal($user); } } return $principals; } /** * Returns a specific principal, specified by it's path. * The returned structure should be the exact same as from * getPrincipalsByPrefix. * * @param string $path * @return array */ public function getPrincipalByPath($path) { $elements = explode('/', $path, 3); if ($elements[0] !== 'principals') { return null; } if ($elements[1] !== 'groups') { return null; } $name = urldecode($elements[2]); $group = $this->groupManager->get($name); if (!is_null($group)) { return $this->groupToPrincipal($group); } return null; } /** * Returns the list of members for a group-principal * * @param string $principal * @return string[] * @throws Exception */ public function getGroupMemberSet($principal) { $elements = explode('/', $principal); if ($elements[0] !== 'principals') { return []; } if ($elements[1] !== 'groups') { return []; } $name = $elements[2]; $group = $this->groupManager->get($name); if (is_null($group)) { return []; } return array_map(function($user) { return $this->userToPrincipal($user); }, $group->getUsers()); } /** * Returns the list of groups a principal is a member of * * @param string $principal * @return array * @throws Exception */ public function getGroupMembership($principal) { return []; } /** * Updates the list of group members for a group principal. * * The principals should be passed as a list of uri's. * * @param string $principal * @param string[] $members * @throws Exception */ public function setGroupMemberSet($principal, array $members) { throw new Exception('Setting members of the group is not supported yet'); } /** * @param string $path * @param PropPatch $propPatch * @return int */ function updatePrincipal($path, PropPatch $propPatch) { return 0; } /** * @param string $prefixPath * @param array $searchProperties * @param string $test * @return array */ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { return []; } /** * @param string $uri * @param string $principalPrefix * @return string */ function findByUri($uri, $principalPrefix) { return ''; } /** * @param IGroup $group * @return array */ protected function groupToPrincipal($group) { $groupId = $group->getGID(); $principal = [ 'uri' => 'principals/groups/' . urlencode($groupId), '{DAV:}displayname' => $groupId, ]; return $principal; } /** * @param IUser $user * @return array */ protected function userToPrincipal($user) { $principal = [ 'uri' => 'principals/users/' . $user->getUID(), '{DAV:}displayname' => $user->getDisplayName(), ]; return $principal; } } DAV/SystemPrincipalBackend.php 0000604 00000012561 15247164651 0012301 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV; use Sabre\DAVACL\PrincipalBackend\AbstractBackend; use Sabre\HTTP\URLUtil; class SystemPrincipalBackend extends AbstractBackend { /** * Returns a list of principals based on a prefix. * * This prefix will often contain something like 'principals'. You are only * expected to return principals that are in this base path. * * You are expected to return at least a 'uri' for every user, you can * return any additional properties if you wish so. Common properties are: * {DAV:}displayname * {http://sabredav.org/ns}email-address - This is a custom SabreDAV * field that's actually injected in a number of other properties. If * you have an email address, use this property. * * @param string $prefixPath * @return array */ function getPrincipalsByPrefix($prefixPath) { $principals = []; if ($prefixPath === 'principals/system') { $principals[] = [ 'uri' => 'principals/system/system', '{DAV:}displayname' => 'system', ]; $principals[] = [ 'uri' => 'principals/system/public', '{DAV:}displayname' => 'public', ]; } return $principals; } /** * Returns a specific principal, specified by it's path. * The returned structure should be the exact same as from * getPrincipalsByPrefix. * * @param string $path * @return array */ function getPrincipalByPath($path) { if ($path === 'principals/system/system') { $principal = [ 'uri' => 'principals/system/system', '{DAV:}displayname' => 'system', ]; return $principal; } if ($path === 'principals/system/public') { $principal = [ 'uri' => 'principals/system/public', '{DAV:}displayname' => 'public', ]; return $principal; } return null; } /** * Updates one ore more webdav properties on a principal. * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documentation for more info and examples. * * @param string $path * @param \Sabre\DAV\PropPatch $propPatch * @return void */ function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) { } /** * This method is used to search for principals matching a set of * properties. * * This search is specifically used by RFC3744's principal-property-search * REPORT. * * The actual search should be a unicode-non-case-sensitive search. The * keys in searchProperties are the WebDAV property names, while the values * are the property values to search on. * * By default, if multiple properties are submitted to this method, the * various properties should be combined with 'AND'. If $test is set to * 'anyof', it should be combined using 'OR'. * * This method should simply return an array with full principal uri's. * * If somebody attempted to search on a property the backend does not * support, you should simply return 0 results. * * You can also just return 0 results if you choose to not support * searching at all, but keep in mind that this may stop certain features * from working. * * @param string $prefixPath * @param array $searchProperties * @param string $test * @return array */ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { return []; } /** * Returns the list of members for a group-principal * * @param string $principal * @return array */ function getGroupMemberSet($principal) { // TODO: for now the group principal has only one member, the user itself $principal = $this->getPrincipalByPath($principal); if (!$principal) { throw new \Sabre\DAV\Exception('Principal not found'); } return [$principal['uri']]; } /** * Returns the list of groups a principal is a member of * * @param string $principal * @return array */ function getGroupMembership($principal) { list($prefix, $name) = URLUtil::splitPath($principal); if ($prefix === 'principals/system') { $principal = $this->getPrincipalByPath($principal); if (!$principal) { throw new \Sabre\DAV\Exception('Principal not found'); } return []; } return []; } /** * Updates the list of group members for a group principal. * * The principals should be passed as a list of uri's. * * @param string $principal * @param array $members * @return void */ function setGroupMemberSet($principal, array $members) { throw new \Sabre\DAV\Exception('Setting members of the group is not supported yet'); } } DAV/CustomPropertiesBackend.php 0000604 00000016611 15247164651 0012502 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @copyright Copyright (c) 2017, Georg Ehrke <oc.list@georgehrke.com> * * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Georg Ehrke <oc.list@georgehrke.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\DAV; use OCP\IDBConnection; use OCP\IUser; use Sabre\DAV\PropertyStorage\Backend\BackendInterface; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; use Sabre\DAV\Tree; class CustomPropertiesBackend implements BackendInterface { /** * Ignored properties * * @var array */ private $ignoredProperties = array( '{DAV:}getcontentlength', '{DAV:}getcontenttype', '{DAV:}getetag', '{DAV:}quota-used-bytes', '{DAV:}quota-available-bytes', '{http://owncloud.org/ns}permissions', '{http://owncloud.org/ns}downloadURL', '{http://owncloud.org/ns}dDC', '{http://owncloud.org/ns}size', ); /** * @var Tree */ private $tree; /** * @var IDBConnection */ private $connection; /** * @var string */ private $user; /** * Properties cache * * @var array */ private $cache = []; /** * @param Tree $tree node tree * @param IDBConnection $connection database connection * @param IUser $user owner of the tree and properties */ public function __construct( Tree $tree, IDBConnection $connection, IUser $user) { $this->tree = $tree; $this->connection = $connection; $this->user = $user->getUID(); } /** * Fetches properties for a path. * * @param string $path * @param PropFind $propFind * @return void */ public function propFind($path, PropFind $propFind) { $requestedProps = $propFind->get404Properties(); // these might appear $requestedProps = array_diff( $requestedProps, $this->ignoredProperties ); // substr of calendars/ => path is inside the CalDAV component // two '/' => this a calendar (no calendar-home nor calendar object) if (substr($path, 0, 10) === 'calendars/' && substr_count($path, '/') === 2) { $allRequestedProps = $propFind->getRequestedProperties(); $customPropertiesForShares = [ '{DAV:}displayname', '{urn:ietf:params:xml:ns:caldav}calendar-description', '{urn:ietf:params:xml:ns:caldav}calendar-timezone', '{http://apple.com/ns/ical/}calendar-order', '{http://apple.com/ns/ical/}calendar-color', '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp', ]; foreach ($customPropertiesForShares as $customPropertyForShares) { if (in_array($customPropertyForShares, $allRequestedProps)) { $requestedProps[] = $customPropertyForShares; } } } if (empty($requestedProps)) { return; } $props = $this->getProperties($path, $requestedProps); foreach ($props as $propName => $propValue) { $propFind->set($propName, $propValue); } } /** * Updates properties for a path * * @param string $path * @param PropPatch $propPatch * * @return void */ public function propPatch($path, PropPatch $propPatch) { $propPatch->handleRemaining(function($changedProps) use ($path) { return $this->updateProperties($path, $changedProps); }); } /** * This method is called after a node is deleted. * * @param string $path path of node for which to delete properties */ public function delete($path) { $statement = $this->connection->prepare( 'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?' ); $statement->execute(array($this->user, $path)); $statement->closeCursor(); unset($this->cache[$path]); } /** * This method is called after a successful MOVE * * @param string $source * @param string $destination * * @return void */ public function move($source, $destination) { $statement = $this->connection->prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?' . ' WHERE `userid` = ? AND `propertypath` = ?' ); $statement->execute(array($destination, $this->user, $source)); $statement->closeCursor(); } /** * Returns a list of properties for this nodes.; * @param string $path * @param array $requestedProperties requested properties or empty array for "all" * @return array * @note The properties list is a list of propertynames the client * requested, encoded as xmlnamespace#tagName, for example: * http://www.example.org/namespace#author If the array is empty, all * properties should be returned */ private function getProperties($path, array $requestedProperties) { if (isset($this->cache[$path])) { return $this->cache[$path]; } // TODO: chunking if more than 1000 properties $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'; $whereValues = array($this->user, $path); $whereTypes = array(null, null); if (!empty($requestedProperties)) { // request only a subset $sql .= ' AND `propertyname` in (?)'; $whereValues[] = $requestedProperties; $whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY; } $result = $this->connection->executeQuery( $sql, $whereValues, $whereTypes ); $props = []; while ($row = $result->fetch()) { $props[$row['propertyname']] = $row['propertyvalue']; } $result->closeCursor(); $this->cache[$path] = $props; return $props; } /** * Update properties * * @param string $path node for which to update properties * @param array $properties array of properties to update * * @return bool */ private function updateProperties($path, $properties) { $deleteStatement = 'DELETE FROM `*PREFIX*properties`' . ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'; $insertStatement = 'INSERT INTO `*PREFIX*properties`' . ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)'; $updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' . ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'; // TODO: use "insert or update" strategy ? $existing = $this->getProperties($path, array()); $this->connection->beginTransaction(); foreach ($properties as $propertyName => $propertyValue) { // If it was null, we need to delete the property if (is_null($propertyValue)) { if (array_key_exists($propertyName, $existing)) { $this->connection->executeUpdate($deleteStatement, array( $this->user, $path, $propertyName ) ); } } else { if (!array_key_exists($propertyName, $existing)) { $this->connection->executeUpdate($insertStatement, array( $this->user, $path, $propertyName, $propertyValue ) ); } else { $this->connection->executeUpdate($updateStatement, array( $propertyValue, $this->user, $path, $propertyName ) ); } } } $this->connection->commit(); unset($this->cache[$path]); return true; } } CardDAV/CardDavBackend.php 0000604 00000104074 15247164651 0011252 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Bjoern Schiessle <bjoern@schiessle.org> * @author Björn Schießle <bjoern@schiessle.org> * @author Georg Ehrke <georg@owncloud.com> * @author Joas Schilling <coding@schilljs.com> * @author Stefan Weil <sw@weilnetz.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OCA\DAV\Connector\Sabre\Principal; use OCP\DB\QueryBuilder\IQueryBuilder; use OCA\DAV\DAV\Sharing\Backend; use OCA\DAV\DAV\Sharing\IShareable; use OCP\IDBConnection; use OCP\IUser; use OCP\IUserManager; use PDO; use Sabre\CardDAV\Backend\BackendInterface; use Sabre\CardDAV\Backend\SyncSupport; use Sabre\CardDAV\Plugin; use Sabre\DAV\Exception\BadRequest; use Sabre\HTTP\URLUtil; use Sabre\VObject\Component\VCard; use Sabre\VObject\Reader; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; class CardDavBackend implements BackendInterface, SyncSupport { const PERSONAL_ADDRESSBOOK_URI = 'contacts'; const PERSONAL_ADDRESSBOOK_NAME = 'Contacts'; /** @var Principal */ private $principalBackend; /** @var string */ private $dbCardsTable = 'cards'; /** @var string */ private $dbCardsPropertiesTable = 'cards_properties'; /** @var IDBConnection */ private $db; /** @var Backend */ private $sharingBackend; /** @var array properties to index */ public static $indexProperties = array( 'BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME', 'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'CLOUD'); /** * @var string[] Map of uid => display name */ protected $userDisplayNames; /** @var IUserManager */ private $userManager; /** @var EventDispatcherInterface */ private $dispatcher; /** * CardDavBackend constructor. * * @param IDBConnection $db * @param Principal $principalBackend * @param IUserManager $userManager * @param EventDispatcherInterface $dispatcher */ public function __construct(IDBConnection $db, Principal $principalBackend, IUserManager $userManager, EventDispatcherInterface $dispatcher) { $this->db = $db; $this->principalBackend = $principalBackend; $this->userManager = $userManager; $this->dispatcher = $dispatcher; $this->sharingBackend = new Backend($this->db, $principalBackend, 'addressbook'); } /** * Return the number of address books for a principal * * @param $principalUri * @return int */ public function getAddressBooksForUserCount($principalUri) { $principalUri = $this->convertPrincipal($principalUri, true); $query = $this->db->getQueryBuilder(); $query->select($query->createFunction('COUNT(*)')) ->from('addressbooks') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))); return (int)$query->execute()->fetchColumn(); } /** * Returns the list of address books for a specific user. * * Every addressbook should have the following properties: * id - an arbitrary unique id * uri - the 'basename' part of the url * principaluri - Same as the passed parameter * * Any additional clark-notation property may be passed besides this. Some * common ones are : * {DAV:}displayname * {urn:ietf:params:xml:ns:carddav}addressbook-description * {http://calendarserver.org/ns/}getctag * * @param string $principalUri * @return array */ function getAddressBooksForUser($principalUri) { $principalUriOriginal = $principalUri; $principalUri = $this->convertPrincipal($principalUri, true); $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken']) ->from('addressbooks') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))); $addressBooks = []; $result = $query->execute(); while($row = $result->fetch()) { $addressBooks[$row['id']] = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $this->convertPrincipal($row['principaluri'], false), '{DAV:}displayname' => $row['displayname'], '{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', ]; $this->addOwnerPrincipal($addressBooks[$row['id']]); } $result->closeCursor(); // query for shared calendars $principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true); $principals = array_map(function($principal) { return urldecode($principal); }, $principals); $principals[]= $principalUri; $query = $this->db->getQueryBuilder(); $result = $query->select(['a.id', 'a.uri', 'a.displayname', 'a.principaluri', 'a.description', 'a.synctoken', 's.access']) ->from('dav_shares', 's') ->join('s', 'addressbooks', 'a', $query->expr()->eq('s.resourceid', 'a.id')) ->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri'))) ->andWhere($query->expr()->eq('s.type', $query->createParameter('type'))) ->setParameter('type', 'addressbook') ->setParameter('principaluri', $principals, IQueryBuilder::PARAM_STR_ARRAY) ->execute(); $readOnlyPropertyName = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only'; while($row = $result->fetch()) { if ($row['principaluri'] === $principalUri) { continue; } $readOnly = (int) $row['access'] === Backend::ACCESS_READ; if (isset($addressBooks[$row['id']])) { if ($readOnly) { // New share can not have more permissions then the old one. continue; } if (isset($addressBooks[$row['id']][$readOnlyPropertyName]) && $addressBooks[$row['id']][$readOnlyPropertyName] === 0) { // Old share is already read-write, no more permissions can be gained continue; } } list(, $name) = URLUtil::splitPath($row['principaluri']); $uri = $row['uri'] . '_shared_by_' . $name; $displayName = $row['displayname'] . ' (' . $this->getUserDisplayName($name) . ')'; $addressBooks[$row['id']] = [ 'id' => $row['id'], 'uri' => $uri, 'principaluri' => $principalUriOriginal, '{DAV:}displayname' => $displayName, '{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'], $readOnlyPropertyName => $readOnly, ]; $this->addOwnerPrincipal($addressBooks[$row['id']]); } $result->closeCursor(); return array_values($addressBooks); } public function getUsersOwnAddressBooks($principalUri) { $principalUri = $this->convertPrincipal($principalUri, true); $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken']) ->from('addressbooks') ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri))); $addressBooks = []; $result = $query->execute(); while($row = $result->fetch()) { $addressBooks[$row['id']] = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $this->convertPrincipal($row['principaluri'], false), '{DAV:}displayname' => $row['displayname'], '{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', ]; $this->addOwnerPrincipal($addressBooks[$row['id']]); } $result->closeCursor(); return array_values($addressBooks); } private function getUserDisplayName($uid) { if (!isset($this->userDisplayNames[$uid])) { $user = $this->userManager->get($uid); if ($user instanceof IUser) { $this->userDisplayNames[$uid] = $user->getDisplayName(); } else { $this->userDisplayNames[$uid] = $uid; } } return $this->userDisplayNames[$uid]; } /** * @param int $addressBookId */ public function getAddressBookById($addressBookId) { $query = $this->db->getQueryBuilder(); $result = $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken']) ->from('addressbooks') ->where($query->expr()->eq('id', $query->createNamedParameter($addressBookId))) ->execute(); $row = $result->fetch(); $result->closeCursor(); if ($row === false) { return null; } $addressBook = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], '{DAV:}displayname' => $row['displayname'], '{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', ]; $this->addOwnerPrincipal($addressBook); return $addressBook; } /** * @param $addressBookUri * @return array|null */ public function getAddressBooksByUri($principal, $addressBookUri) { $query = $this->db->getQueryBuilder(); $result = $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken']) ->from('addressbooks') ->where($query->expr()->eq('uri', $query->createNamedParameter($addressBookUri))) ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) ->setMaxResults(1) ->execute(); $row = $result->fetch(); $result->closeCursor(); if ($row === false) { return null; } $addressBook = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], '{DAV:}displayname' => $row['displayname'], '{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', ]; $this->addOwnerPrincipal($addressBook); return $addressBook; } /** * Updates properties for an address book. * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documentation for more info and examples. * * @param string $addressBookId * @param \Sabre\DAV\PropPatch $propPatch * @return void */ function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) { $supportedProperties = [ '{DAV:}displayname', '{' . Plugin::NS_CARDDAV . '}addressbook-description', ]; $propPatch->handle($supportedProperties, function($mutations) use ($addressBookId) { $updates = []; foreach($mutations as $property=>$newValue) { switch($property) { case '{DAV:}displayname' : $updates['displayname'] = $newValue; break; case '{' . Plugin::NS_CARDDAV . '}addressbook-description' : $updates['description'] = $newValue; break; } } $query = $this->db->getQueryBuilder(); $query->update('addressbooks'); foreach($updates as $key=>$value) { $query->set($key, $query->createNamedParameter($value)); } $query->where($query->expr()->eq('id', $query->createNamedParameter($addressBookId))) ->execute(); $this->addChange($addressBookId, "", 2); return true; }); } /** * Creates a new address book * * @param string $principalUri * @param string $url Just the 'basename' of the url. * @param array $properties * @return int * @throws BadRequest */ function createAddressBook($principalUri, $url, array $properties) { $values = [ 'displayname' => null, 'description' => null, 'principaluri' => $principalUri, 'uri' => $url, 'synctoken' => 1 ]; foreach($properties as $property=>$newValue) { switch($property) { case '{DAV:}displayname' : $values['displayname'] = $newValue; break; case '{' . Plugin::NS_CARDDAV . '}addressbook-description' : $values['description'] = $newValue; break; default : throw new BadRequest('Unknown property: ' . $property); } } // Fallback to make sure the displayname is set. Some clients may refuse // to work with addressbooks not having a displayname. if(is_null($values['displayname'])) { $values['displayname'] = $url; } $query = $this->db->getQueryBuilder(); $query->insert('addressbooks') ->values([ 'uri' => $query->createParameter('uri'), 'displayname' => $query->createParameter('displayname'), 'description' => $query->createParameter('description'), 'principaluri' => $query->createParameter('principaluri'), 'synctoken' => $query->createParameter('synctoken'), ]) ->setParameters($values) ->execute(); return $query->getLastInsertId(); } /** * Deletes an entire addressbook and all its contents * * @param mixed $addressBookId * @return void */ function deleteAddressBook($addressBookId) { $query = $this->db->getQueryBuilder(); $query->delete('cards') ->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid'))) ->setParameter('addressbookid', $addressBookId) ->execute(); $query->delete('addressbookchanges') ->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid'))) ->setParameter('addressbookid', $addressBookId) ->execute(); $query->delete('addressbooks') ->where($query->expr()->eq('id', $query->createParameter('id'))) ->setParameter('id', $addressBookId) ->execute(); $this->sharingBackend->deleteAllShares($addressBookId); $query->delete($this->dbCardsPropertiesTable) ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->execute(); } /** * Returns all cards for a specific addressbook id. * * This method should return the following properties for each card: * * carddata - raw vcard data * * uri - Some unique url * * lastmodified - A unix timestamp * * It's recommended to also return the following properties: * * etag - A unique etag. This must change every time the card changes. * * size - The size of the card in bytes. * * If these last two properties are provided, less time will be spent * calculating them. If they are specified, you can also ommit carddata. * This may speed up certain requests, especially with large cards. * * @param mixed $addressBookId * @return array */ function getCards($addressBookId) { $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata']) ->from('cards') ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))); $cards = []; $result = $query->execute(); while($row = $result->fetch()) { $row['etag'] = '"' . $row['etag'] . '"'; $row['carddata'] = $this->readBlob($row['carddata']); $cards[] = $row; } $result->closeCursor(); return $cards; } /** * Returns a specific card. * * The same set of properties must be returned as with getCards. The only * exception is that 'carddata' is absolutely required. * * If the card does not exist, you must return false. * * @param mixed $addressBookId * @param string $cardUri * @return array */ function getCard($addressBookId, $cardUri) { $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata']) ->from('cards') ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri))) ->setMaxResults(1); $result = $query->execute(); $row = $result->fetch(); if (!$row) { return false; } $row['etag'] = '"' . $row['etag'] . '"'; $row['carddata'] = $this->readBlob($row['carddata']); return $row; } /** * Returns a list of cards. * * This method should work identical to getCard, but instead return all the * cards in the list as an array. * * If the backend supports this, it may allow for some speed-ups. * * @param mixed $addressBookId * @param string[] $uris * @return array */ function getMultipleCards($addressBookId, array $uris) { if (empty($uris)) { return []; } $chunks = array_chunk($uris, 100); $cards = []; $query = $this->db->getQueryBuilder(); $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata']) ->from('cards') ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->andWhere($query->expr()->in('uri', $query->createParameter('uri'))); foreach ($chunks as $uris) { $query->setParameter('uri', $uris, IQueryBuilder::PARAM_STR_ARRAY); $result = $query->execute(); while ($row = $result->fetch()) { $row['etag'] = '"' . $row['etag'] . '"'; $row['carddata'] = $this->readBlob($row['carddata']); $cards[] = $row; } $result->closeCursor(); } return $cards; } /** * Creates a new card. * * The addressbook id will be passed as the first argument. This is the * same id as it is returned from the getAddressBooksForUser method. * * The cardUri is a base uri, and doesn't include the full path. The * cardData argument is the vcard body, and is passed as a string. * * It is possible to return an ETag from this method. This ETag is for the * newly created resource, and must be enclosed with double quotes (that * is, the string itself must contain the double quotes). * * You should only return the ETag if you store the carddata as-is. If a * subsequent GET request on the same card does not have the same body, * byte-by-byte and you did return an ETag here, clients tend to get * confused. * * If you don't return an ETag, you can just return null. * * @param mixed $addressBookId * @param string $cardUri * @param string $cardData * @return string */ function createCard($addressBookId, $cardUri, $cardData) { $etag = md5($cardData); $query = $this->db->getQueryBuilder(); $query->insert('cards') ->values([ 'carddata' => $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB), 'uri' => $query->createNamedParameter($cardUri), 'lastmodified' => $query->createNamedParameter(time()), 'addressbookid' => $query->createNamedParameter($addressBookId), 'size' => $query->createNamedParameter(strlen($cardData)), 'etag' => $query->createNamedParameter($etag), ]) ->execute(); $this->addChange($addressBookId, $cardUri, 1); $this->updateProperties($addressBookId, $cardUri, $cardData); $this->dispatcher->dispatch('\OCA\DAV\CardDAV\CardDavBackend::createCard', new GenericEvent(null, [ 'addressBookId' => $addressBookId, 'cardUri' => $cardUri, 'cardData' => $cardData])); return '"' . $etag . '"'; } /** * Updates a card. * * The addressbook id will be passed as the first argument. This is the * same id as it is returned from the getAddressBooksForUser method. * * The cardUri is a base uri, and doesn't include the full path. The * cardData argument is the vcard body, and is passed as a string. * * It is possible to return an ETag from this method. This ETag should * match that of the updated resource, and must be enclosed with double * quotes (that is: the string itself must contain the actual quotes). * * You should only return the ETag if you store the carddata as-is. If a * subsequent GET request on the same card does not have the same body, * byte-by-byte and you did return an ETag here, clients tend to get * confused. * * If you don't return an ETag, you can just return null. * * @param mixed $addressBookId * @param string $cardUri * @param string $cardData * @return string */ function updateCard($addressBookId, $cardUri, $cardData) { $etag = md5($cardData); $query = $this->db->getQueryBuilder(); $query->update('cards') ->set('carddata', $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB)) ->set('lastmodified', $query->createNamedParameter(time())) ->set('size', $query->createNamedParameter(strlen($cardData))) ->set('etag', $query->createNamedParameter($etag)) ->where($query->expr()->eq('uri', $query->createNamedParameter($cardUri))) ->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->execute(); $this->addChange($addressBookId, $cardUri, 2); $this->updateProperties($addressBookId, $cardUri, $cardData); $this->dispatcher->dispatch('\OCA\DAV\CardDAV\CardDavBackend::updateCard', new GenericEvent(null, [ 'addressBookId' => $addressBookId, 'cardUri' => $cardUri, 'cardData' => $cardData])); return '"' . $etag . '"'; } /** * Deletes a card * * @param mixed $addressBookId * @param string $cardUri * @return bool */ function deleteCard($addressBookId, $cardUri) { try { $cardId = $this->getCardId($addressBookId, $cardUri); } catch (\InvalidArgumentException $e) { $cardId = null; } $query = $this->db->getQueryBuilder(); $ret = $query->delete('cards') ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri))) ->execute(); $this->addChange($addressBookId, $cardUri, 3); $this->dispatcher->dispatch('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', new GenericEvent(null, [ 'addressBookId' => $addressBookId, 'cardUri' => $cardUri])); if ($ret === 1) { if ($cardId !== null) { $this->purgeProperties($addressBookId, $cardId); } return true; } return false; } /** * The getChanges method returns all the changes that have happened, since * the specified syncToken in the specified address book. * * This function should return an array, such as the following: * * [ * 'syncToken' => 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'modified.txt', * ], * 'deleted' => [ * 'foo.php.bak', * 'old.txt' * ] * ]; * * The returned syncToken property should reflect the *current* syncToken * of the calendar, as reported in the {http://sabredav.org/ns}sync-token * property. This is needed here too, to ensure the operation is atomic. * * If the $syncToken argument is specified as null, this is an initial * sync, and all members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The $syncLevel argument is basically the 'depth' of the report. If it's * 1, you only have to report changes that happened only directly in * immediate descendants. If it's 2, it should also include changes from * the nodes below the child collections. (grandchildren) * * The $limit argument allows a client to specify how many results should * be returned at most. If the limit is not specified, it should be treated * as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $addressBookId * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) { // Current synctoken $stmt = $this->db->prepare('SELECT `synctoken` FROM `*PREFIX*addressbooks` WHERE `id` = ?'); $stmt->execute([ $addressBookId ]); $currentToken = $stmt->fetchColumn(0); if (is_null($currentToken)) return null; $result = [ 'syncToken' => $currentToken, 'added' => [], 'modified' => [], 'deleted' => [], ]; if ($syncToken) { $query = "SELECT `uri`, `operation` FROM `*PREFIX*addressbookchanges` WHERE `synctoken` >= ? AND `synctoken` < ? AND `addressbookid` = ? ORDER BY `synctoken`"; if ($limit>0) { $query .= " `LIMIT` " . (int)$limit; } // Fetching all changes $stmt = $this->db->prepare($query); $stmt->execute([$syncToken, $currentToken, $addressBookId]); $changes = []; // This loop ensures that any duplicates are overwritten, only the // last change on a node is relevant. while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $changes[$row['uri']] = $row['operation']; } foreach($changes as $uri => $operation) { switch($operation) { case 1: $result['added'][] = $uri; break; case 2: $result['modified'][] = $uri; break; case 3: $result['deleted'][] = $uri; break; } } } else { // No synctoken supplied, this is the initial sync. $query = "SELECT `uri` FROM `*PREFIX*cards` WHERE `addressbookid` = ?"; $stmt = $this->db->prepare($query); $stmt->execute([$addressBookId]); $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN); } return $result; } /** * Adds a change record to the addressbookchanges table. * * @param mixed $addressBookId * @param string $objectUri * @param int $operation 1 = add, 2 = modify, 3 = delete * @return void */ protected function addChange($addressBookId, $objectUri, $operation) { $sql = 'INSERT INTO `*PREFIX*addressbookchanges`(`uri`, `synctoken`, `addressbookid`, `operation`) SELECT ?, `synctoken`, ?, ? FROM `*PREFIX*addressbooks` WHERE `id` = ?'; $stmt = $this->db->prepare($sql); $stmt->execute([ $objectUri, $addressBookId, $operation, $addressBookId ]); $stmt = $this->db->prepare('UPDATE `*PREFIX*addressbooks` SET `synctoken` = `synctoken` + 1 WHERE `id` = ?'); $stmt->execute([ $addressBookId ]); } private function readBlob($cardData) { if (is_resource($cardData)) { return stream_get_contents($cardData); } return $cardData; } /** * @param IShareable $shareable * @param string[] $add * @param string[] $remove */ public function updateShares(IShareable $shareable, $add, $remove) { $this->sharingBackend->updateShares($shareable, $add, $remove); } /** * search contact * * @param int $addressBookId * @param string $pattern which should match within the $searchProperties * @param array $searchProperties defines the properties within the query pattern should match * @return array an array of contacts which are arrays of key-value-pairs */ public function search($addressBookId, $pattern, $searchProperties) { $query = $this->db->getQueryBuilder(); $query2 = $this->db->getQueryBuilder(); $query2->selectDistinct('cp.cardid')->from($this->dbCardsPropertiesTable, 'cp'); $query2->andWhere($query2->expr()->eq('cp.addressbookid', $query->createNamedParameter($addressBookId))); $or = $query2->expr()->orX(); foreach ($searchProperties as $property) { $or->add($query2->expr()->eq('cp.name', $query->createNamedParameter($property))); } $query2->andWhere($or); $query2->andWhere($query2->expr()->ilike('cp.value', $query->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%'))); $query->select('c.carddata', 'c.uri')->from($this->dbCardsTable, 'c') ->where($query->expr()->in('c.id', $query->createFunction($query2->getSQL()))); $result = $query->execute(); $cards = $result->fetchAll(); $result->closeCursor(); return array_map(function($array) { $array['carddata'] = $this->readBlob($array['carddata']); return $array; }, $cards); } /** * @param int $bookId * @param string $name * @return array */ public function collectCardProperties($bookId, $name) { $query = $this->db->getQueryBuilder(); $result = $query->selectDistinct('value') ->from($this->dbCardsPropertiesTable) ->where($query->expr()->eq('name', $query->createNamedParameter($name))) ->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($bookId))) ->execute(); $all = $result->fetchAll(PDO::FETCH_COLUMN); $result->closeCursor(); return $all; } /** * get URI from a given contact * * @param int $id * @return string */ public function getCardUri($id) { $query = $this->db->getQueryBuilder(); $query->select('uri')->from($this->dbCardsTable) ->where($query->expr()->eq('id', $query->createParameter('id'))) ->setParameter('id', $id); $result = $query->execute(); $uri = $result->fetch(); $result->closeCursor(); if (!isset($uri['uri'])) { throw new \InvalidArgumentException('Card does not exists: ' . $id); } return $uri['uri']; } /** * return contact with the given URI * * @param int $addressBookId * @param string $uri * @returns array */ public function getContact($addressBookId, $uri) { $result = []; $query = $this->db->getQueryBuilder(); $query->select('*')->from($this->dbCardsTable) ->where($query->expr()->eq('uri', $query->createNamedParameter($uri))) ->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))); $queryResult = $query->execute(); $contact = $queryResult->fetch(); $queryResult->closeCursor(); if (is_array($contact)) { $result = $contact; } return $result; } /** * Returns the list of people whom this address book is shared with. * * Every element in this array should have the following properties: * * href - Often a mailto: address * * commonName - Optional, for example a first + last name * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. * * readOnly - boolean * * summary - Optional, a description for the share * * @return array */ public function getShares($addressBookId) { return $this->sharingBackend->getShares($addressBookId); } /** * update properties table * * @param int $addressBookId * @param string $cardUri * @param string $vCardSerialized */ protected function updateProperties($addressBookId, $cardUri, $vCardSerialized) { $cardId = $this->getCardId($addressBookId, $cardUri); $vCard = $this->readCard($vCardSerialized); $this->purgeProperties($addressBookId, $cardId); $query = $this->db->getQueryBuilder(); $query->insert($this->dbCardsPropertiesTable) ->values( [ 'addressbookid' => $query->createNamedParameter($addressBookId), 'cardid' => $query->createNamedParameter($cardId), 'name' => $query->createParameter('name'), 'value' => $query->createParameter('value'), 'preferred' => $query->createParameter('preferred') ] ); foreach ($vCard->children() as $property) { if(!in_array($property->name, self::$indexProperties)) { continue; } $preferred = 0; foreach($property->parameters as $parameter) { if ($parameter->name == 'TYPE' && strtoupper($parameter->getValue()) == 'PREF') { $preferred = 1; break; } } $query->setParameter('name', $property->name); $query->setParameter('value', substr($property->getValue(), 0, 254)); $query->setParameter('preferred', $preferred); $query->execute(); } } /** * read vCard data into a vCard object * * @param string $cardData * @return VCard */ protected function readCard($cardData) { return Reader::read($cardData); } /** * delete all properties from a given card * * @param int $addressBookId * @param int $cardId */ protected function purgeProperties($addressBookId, $cardId) { $query = $this->db->getQueryBuilder(); $query->delete($this->dbCardsPropertiesTable) ->where($query->expr()->eq('cardid', $query->createNamedParameter($cardId))) ->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))); $query->execute(); } /** * get ID from a given contact * * @param int $addressBookId * @param string $uri * @return int */ protected function getCardId($addressBookId, $uri) { $query = $this->db->getQueryBuilder(); $query->select('id')->from($this->dbCardsTable) ->where($query->expr()->eq('uri', $query->createNamedParameter($uri))) ->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))); $result = $query->execute(); $cardIds = $result->fetch(); $result->closeCursor(); if (!isset($cardIds['id'])) { throw new \InvalidArgumentException('Card does not exists: ' . $uri); } return (int)$cardIds['id']; } /** * For shared address books the sharee is set in the ACL of the address book * @param $addressBookId * @param $acl * @return array */ public function applyShareAcl($addressBookId, $acl) { return $this->sharingBackend->applyShareAcl($addressBookId, $acl); } private function convertPrincipal($principalUri, $toV2) { if ($this->principalBackend->getPrincipalPrefix() === 'principals') { list(, $name) = URLUtil::splitPath($principalUri); if ($toV2 === true) { return "principals/users/$name"; } return "principals/$name"; } return $principalUri; } private function addOwnerPrincipal(&$addressbookInfo) { $ownerPrincipalKey = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal'; $displaynameKey = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}owner-displayname'; if (isset($addressbookInfo[$ownerPrincipalKey])) { $uri = $addressbookInfo[$ownerPrincipalKey]; } else { $uri = $addressbookInfo['principaluri']; } $principalInformation = $this->principalBackend->getPrincipalByPath($uri); if (isset($principalInformation['{DAV:}displayname'])) { $addressbookInfo[$displaynameKey] = $principalInformation['{DAV:}displayname']; } } } CardDAV/AddressBookRoot.php 0000604 00000004142 15247164651 0011535 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OCP\IL10N; class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot { /** @var IL10N */ protected $l10n; /** * @param \Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend * @param \Sabre\CardDAV\Backend\BackendInterface $carddavBackend * @param string $principalPrefix */ public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend, \Sabre\CardDAV\Backend\BackendInterface $carddavBackend, $principalPrefix = 'principals') { parent::__construct($principalBackend, $carddavBackend, $principalPrefix); $this->l10n = \OC::$server->getL10N('dav'); } /** * This method returns a node for a principal. * * The passed array contains principal information, and is guaranteed to * at least contain a uri item. Other properties may or may not be * supplied by the authentication backend. * * @param array $principal * @return \Sabre\DAV\INode */ function getChildForPrincipal(array $principal) { return new UserAddressBooks($this->carddavBackend, $principal['uri'], $this->l10n); } function getName() { if ($this->principalPrefix === 'principals') { return parent::getName(); } // Grabbing all the components of the principal path. $parts = explode('/', $this->principalPrefix); // We are only interested in the second part. return $parts[1]; } } CardDAV/SyncService.php 0000604 00000022663 15247164651 0010736 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OC\Accounts\AccountManager; use OCP\AppFramework\Http; use OCP\ICertificateManager; use OCP\ILogger; use OCP\IUser; use OCP\IUserManager; use Sabre\DAV\Client; use Sabre\DAV\Xml\Response\MultiStatus; use Sabre\DAV\Xml\Service; use Sabre\HTTP\ClientHttpException; use Sabre\VObject\Reader; class SyncService { /** @var CardDavBackend */ private $backend; /** @var IUserManager */ private $userManager; /** @var ILogger */ private $logger; /** @var array */ private $localSystemAddressBook; /** @var AccountManager */ private $accountManager; /** @var string */ protected $certPath; /** * SyncService constructor. * * @param CardDavBackend $backend * @param IUserManager $userManager * @param ILogger $logger * @param AccountManager $accountManager */ public function __construct(CardDavBackend $backend, IUserManager $userManager, ILogger $logger, AccountManager $accountManager) { $this->backend = $backend; $this->userManager = $userManager; $this->logger = $logger; $this->accountManager = $accountManager; $this->certPath = ''; } /** * @param string $url * @param string $userName * @param string $addressBookUrl * @param string $sharedSecret * @param string $syncToken * @param int $targetBookId * @param string $targetPrincipal * @param array $targetProperties * @return string * @throws \Exception */ public function syncRemoteAddressBook($url, $userName, $addressBookUrl, $sharedSecret, $syncToken, $targetBookId, $targetPrincipal, $targetProperties) { // 1. create addressbook $book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookId, $targetProperties); $addressBookId = $book['id']; // 2. query changes try { $response = $this->requestSyncReport($url, $userName, $addressBookUrl, $sharedSecret, $syncToken); } catch (ClientHttpException $ex) { if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) { // remote server revoked access to the address book, remove it $this->backend->deleteAddressBook($addressBookId); $this->logger->info('Authorization failed, remove address book: ' . $url, ['app' => 'dav']); throw $ex; } } // 3. apply changes // TODO: use multi-get for download foreach ($response['response'] as $resource => $status) { $cardUri = basename($resource); if (isset($status[200])) { $vCard = $this->download($url, $userName, $sharedSecret, $resource); $existingCard = $this->backend->getCard($addressBookId, $cardUri); if ($existingCard === false) { $this->backend->createCard($addressBookId, $cardUri, $vCard['body']); } else { $this->backend->updateCard($addressBookId, $cardUri, $vCard['body']); } } else { $this->backend->deleteCard($addressBookId, $cardUri); } } return $response['token']; } /** * @param string $principal * @param string $id * @param array $properties * @return array|null * @throws \Sabre\DAV\Exception\BadRequest */ public function ensureSystemAddressBookExists($principal, $id, $properties) { $book = $this->backend->getAddressBooksByUri($principal, $id); if (!is_null($book)) { return $book; } $this->backend->createAddressBook($principal, $id, $properties); return $this->backend->getAddressBooksByUri($principal, $id); } /** * Check if there is a valid certPath we should use * * @return string */ protected function getCertPath() { // we already have a valid certPath if ($this->certPath !== '') { return $this->certPath; } /** @var ICertificateManager $certManager */ $certManager = \OC::$server->getCertificateManager(null); $certPath = $certManager->getAbsoluteBundlePath(); if (file_exists($certPath)) { $this->certPath = $certPath; } return $this->certPath; } /** * @param string $url * @param string $userName * @param string $addressBookUrl * @param string $sharedSecret * @return Client */ protected function getClient($url, $userName, $sharedSecret) { $settings = [ 'baseUri' => $url . '/', 'userName' => $userName, 'password' => $sharedSecret, ]; $client = new Client($settings); $certPath = $this->getCertPath(); $client->setThrowExceptions(true); if ($certPath !== '' && strpos($url, 'http://') !== 0) { $client->addCurlSetting(CURLOPT_CAINFO, $this->certPath); } return $client; } /** * @param string $url * @param string $userName * @param string $addressBookUrl * @param string $sharedSecret * @param string $syncToken * @return array */ protected function requestSyncReport($url, $userName, $addressBookUrl, $sharedSecret, $syncToken) { $client = $this->getClient($url, $userName, $sharedSecret); $body = $this->buildSyncCollectionRequestBody($syncToken); $response = $client->request('REPORT', $addressBookUrl, $body, [ 'Content-Type' => 'application/xml' ]); return $this->parseMultiStatus($response['body']); } /** * @param string $url * @param string $userName * @param string $sharedSecret * @param string $resourcePath * @return array */ protected function download($url, $userName, $sharedSecret, $resourcePath) { $client = $this->getClient($url, $userName, $sharedSecret); return $client->request('GET', $resourcePath); } /** * @param string|null $syncToken * @return string */ private function buildSyncCollectionRequestBody($syncToken) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $root = $dom->createElementNS('DAV:', 'd:sync-collection'); $sync = $dom->createElement('d:sync-token', $syncToken); $prop = $dom->createElement('d:prop'); $cont = $dom->createElement('d:getcontenttype'); $etag = $dom->createElement('d:getetag'); $prop->appendChild($cont); $prop->appendChild($etag); $root->appendChild($sync); $root->appendChild($prop); $dom->appendChild($root); $body = $dom->saveXML(); return $body; } /** * @param string $body * @return array * @throws \Sabre\Xml\ParseException */ private function parseMultiStatus($body) { $xml = new Service(); /** @var MultiStatus $multiStatus */ $multiStatus = $xml->expect('{DAV:}multistatus', $body); $result = []; foreach ($multiStatus->getResponses() as $response) { $result[$response->getHref()] = $response->getResponseProperties(); } return ['response' => $result, 'token' => $multiStatus->getSyncToken()]; } /** * @param IUser $user */ public function updateUser($user) { $systemAddressBook = $this->getLocalSystemAddressBook(); $addressBookId = $systemAddressBook['id']; $converter = new Converter($this->accountManager); $name = $user->getBackendClassName(); $userId = $user->getUID(); $cardId = "$name:$userId.vcf"; $card = $this->backend->getCard($addressBookId, $cardId); if ($card === false) { $vCard = $converter->createCardFromUser($user); if ($vCard !== null) { $this->backend->createCard($addressBookId, $cardId, $vCard->serialize()); } } else { $vCard = $converter->createCardFromUser($user); if (is_null($vCard)) { $this->backend->deleteCard($addressBookId, $cardId); } else { $this->backend->updateCard($addressBookId, $cardId, $vCard->serialize()); } } } /** * @param IUser|string $userOrCardId */ public function deleteUser($userOrCardId) { $systemAddressBook = $this->getLocalSystemAddressBook(); if ($userOrCardId instanceof IUser){ $name = $userOrCardId->getBackendClassName(); $userId = $userOrCardId->getUID(); $userOrCardId = "$name:$userId.vcf"; } $this->backend->deleteCard($systemAddressBook['id'], $userOrCardId); } /** * @return array|null */ public function getLocalSystemAddressBook() { if (is_null($this->localSystemAddressBook)) { $systemPrincipal = "principals/system/system"; $this->localSystemAddressBook = $this->ensureSystemAddressBookExists($systemPrincipal, 'system', [ '{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance' ]); } return $this->localSystemAddressBook; } public function syncInstance(\Closure $progressCallback = null) { $systemAddressBook = $this->getLocalSystemAddressBook(); $this->userManager->callForAllUsers(function($user) use ($systemAddressBook, $progressCallback) { $this->updateUser($user); if (!is_null($progressCallback)) { $progressCallback(); } }); // remove no longer existing $allCards = $this->backend->getCards($systemAddressBook['id']); foreach($allCards as $card) { $vCard = Reader::read($card['carddata']); $uid = $vCard->UID->getValue(); // load backend and see if user exists if (!$this->userManager->userExists($uid)) { $this->deleteUser($card['uri']); } } } } CardDAV/PhotoCache.php 0000604 00000012105 15247164651 0010504 0 ustar 00 <?php namespace OCA\DAV\CardDAV; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; use Sabre\CardDAV\Card; use Sabre\VObject\Property\Binary; use Sabre\VObject\Reader; class PhotoCache { /** @var IAppData $appData */ protected $appData; /** * PhotoCache constructor. * * @param IAppData $appData */ public function __construct(IAppData $appData) { $this->appData = $appData; } /** * @param int $addressBookId * @param string $cardUri * @param int $size * @param Card $card * * @return ISimpleFile * @throws NotFoundException */ public function get($addressBookId, $cardUri, $size, Card $card) { $folder = $this->getFolder($addressBookId, $cardUri); if ($this->isEmpty($folder)) { $this->init($folder, $card); } if (!$this->hasPhoto($folder)) { throw new NotFoundException(); } if ($size !== -1) { $size = 2 ** ceil(log($size) / log(2)); } return $this->getFile($folder, $size); } /** * @param ISimpleFolder $folder * @return bool */ private function isEmpty(ISimpleFolder $folder) { return $folder->getDirectoryListing() === []; } /** * @param ISimpleFolder $folder * @param Card $card */ private function init(ISimpleFolder $folder, Card $card) { $data = $this->getPhoto($card); if ($data === false) { $folder->newFile('nophoto'); } else { switch ($data['Content-Type']) { case 'image/png': $ext = 'png'; break; case 'image/jpeg': $ext = 'jpg'; break; case 'image/gif': $ext = 'gif'; break; } $file = $folder->newFile('photo.' . $ext); $file->putContent($data['body']); } } private function hasPhoto(ISimpleFolder $folder) { return !$folder->fileExists('nophoto'); } private function getFile(ISimpleFolder $folder, $size) { $ext = $this->getExtension($folder); if ($size === -1) { $path = 'photo.' . $ext; } else { $path = 'photo.' . $size . '.' . $ext; } try { $file = $folder->getFile($path); } catch (NotFoundException $e) { if ($size <= 0) { throw new NotFoundException; } $photo = new \OC_Image(); /** @var ISimpleFile $file */ $file = $folder->getFile('photo.' . $ext); $photo->loadFromData($file->getContent()); $ratio = $photo->width() / $photo->height(); if ($ratio < 1) { $ratio = 1/$ratio; } $size = (int)($size * $ratio); if ($size !== -1) { $photo->resize($size); } try { $file = $folder->newFile($path); $file->putContent($photo->data()); } catch (NotPermittedException $e) { } } return $file; } /** * @param int $addressBookId * @param string $cardUri * @return ISimpleFolder */ private function getFolder($addressBookId, $cardUri) { $hash = md5($addressBookId . ' ' . $cardUri); try { return $this->appData->getFolder($hash); } catch (NotFoundException $e) { return $this->appData->newFolder($hash); } } /** * Get the extension of the avatar. If there is no avatar throw Exception * * @param ISimpleFolder $folder * @return string * @throws NotFoundException */ private function getExtension(ISimpleFolder $folder) { if ($folder->fileExists('photo.jpg')) { return 'jpg'; } elseif ($folder->fileExists('photo.png')) { return 'png'; } elseif ($folder->fileExists('photo.gif')) { return 'gif'; } throw new NotFoundException; } private function getPhoto(Card $node) { try { $vObject = $this->readCard($node->get()); if (!$vObject->PHOTO) { return false; } $photo = $vObject->PHOTO; $type = $this->getType($photo); $val = $photo->getValue(); if ($photo->getValueType() === 'URI') { $parsed = \Sabre\URI\parse($val); //only allow data:// if ($parsed['scheme'] !== 'data') { return false; } if (substr_count($parsed['path'], ';') === 1) { list($type,) = explode(';', $parsed['path']); } $val = file_get_contents($val); } $allowedContentTypes = [ 'image/png', 'image/jpeg', 'image/gif', ]; if(!in_array($type, $allowedContentTypes, true)) { $type = 'application/octet-stream'; } return [ 'Content-Type' => $type, 'body' => $val ]; } catch(\Exception $ex) { } return false; } /** * @param string $cardData * @return \Sabre\VObject\Document */ private function readCard($cardData) { return Reader::read($cardData); } /** * @param Binary $photo * @return string */ private function getType(Binary $photo) { $params = $photo->parameters(); if (isset($params['TYPE']) || isset($params['MEDIATYPE'])) { /** @var Parameter $typeParam */ $typeParam = isset($params['TYPE']) ? $params['TYPE'] : $params['MEDIATYPE']; $type = $typeParam->getValue(); if (strpos($type, 'image/') === 0) { return $type; } else { return 'image/' . strtolower($type); } } return ''; } /** * @param int $addressBookId * @param string $cardUri */ public function delete($addressBookId, $cardUri) { $folder = $this->getFolder($addressBookId, $cardUri); $folder->delete(); } } CardDAV/Plugin.php 0000604 00000004431 15247164651 0007730 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OCA\DAV\CardDAV\Xml\Groups; use Sabre\DAV\INode; use Sabre\DAV\PropFind; use Sabre\DAV\Server; use Sabre\HTTP\URLUtil; class Plugin extends \Sabre\CardDAV\Plugin { function initialize(Server $server) { $server->on('propFind', [$this, 'propFind']); parent::initialize($server); } /** * Returns the addressbook home for a given principal * * @param string $principal * @return string */ protected function getAddressbookHomeForPrincipal($principal) { if (strrpos($principal, 'principals/users', -strlen($principal)) !== false) { list(, $principalId) = URLUtil::splitPath($principal); return self::ADDRESSBOOK_ROOT . '/users/' . $principalId; } if (strrpos($principal, 'principals/groups', -strlen($principal)) !== false) { list(, $principalId) = URLUtil::splitPath($principal); return self::ADDRESSBOOK_ROOT . '/groups/' . $principalId; } if (strrpos($principal, 'principals/system', -strlen($principal)) !== false) { list(, $principalId) = URLUtil::splitPath($principal); return self::ADDRESSBOOK_ROOT . '/system/' . $principalId; } throw new \LogicException('This is not supposed to happen'); } /** * Adds all CardDAV-specific properties * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { $ns = '{http://owncloud.org/ns}'; if ($node instanceof AddressBook) { $propFind->handle($ns . 'groups', function () use ($node) { return new Groups($node->getContactsGroups()); }); } } } CardDAV/ImageExportPlugin.php 0000604 00000006274 15247164651 0012104 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OCP\Files\NotFoundException; use OCP\ILogger; use Sabre\CardDAV\Card; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; class ImageExportPlugin extends ServerPlugin { /** @var Server */ protected $server; /** @var PhotoCache */ private $cache; /** * ImageExportPlugin constructor. * * @param PhotoCache $cache */ public function __construct(PhotoCache $cache) { $this->cache = $cache; } /** * Initializes the plugin and registers event handlers * * @param Server $server * @return void */ public function initialize(Server $server) { $this->server = $server; $this->server->on('method:GET', [$this, 'httpGet'], 90); } /** * Intercepts GET requests on addressbook urls ending with ?photo. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ public function httpGet(RequestInterface $request, ResponseInterface $response) { $queryParams = $request->getQueryParameters(); // TODO: in addition to photo we should also add logo some point in time if (!array_key_exists('photo', $queryParams)) { return true; } $size = isset($queryParams['size']) ? (int)$queryParams['size'] : -1; $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path); if (!($node instanceof Card)) { return true; } $this->server->transactionType = 'carddav-image-export'; // Checking ACL, if available. if ($aclPlugin = $this->server->getPlugin('acl')) { /** @var \Sabre\DAVACL\Plugin $aclPlugin */ $aclPlugin->checkPrivileges($path, '{DAV:}read'); } // Fetch addressbook $addressbookpath = explode('/', $path); array_pop($addressbookpath); $addressbookpath = implode('/', $addressbookpath); /** @var AddressBook $addressbook */ $addressbook = $this->server->tree->getNodeForPath($addressbookpath); $response->setHeader('Cache-Control', 'private, max-age=3600, must-revalidate'); $response->setHeader('Etag', $node->getETag() ); $response->setHeader('Pragma', 'public'); try { $file = $this->cache->get($addressbook->getResourceId(), $node->getName(), $size, $node); $response->setHeader('Content-Type', $file->getMimeType()); $response->setHeader('Content-Disposition', 'attachment'); $response->setStatus(200); $response->setBody($file->getContent()); } catch (NotFoundException $e) { $response->setStatus(404); } return false; } } CardDAV/Converter.php 0000604 00000010147 15247164651 0010442 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OC\Accounts\AccountManager; use OCP\IImage; use OCP\IUser; use Sabre\VObject\Component\VCard; use Sabre\VObject\Property\Text; class Converter { /** @var AccountManager */ private $accountManager; /** * Converter constructor. * * @param AccountManager $accountManager */ public function __construct(AccountManager $accountManager) { $this->accountManager = $accountManager; } /** * @param IUser $user * @return VCard|null */ public function createCardFromUser(IUser $user) { $userData = $this->accountManager->getUser($user); $uid = $user->getUID(); $cloudId = $user->getCloudId(); $image = $this->getAvatarImage($user); $vCard = new VCard(); $vCard->VERSION = '3.0'; $vCard->UID = $uid; $publish = false; foreach ($userData as $property => $value) { $shareWithTrustedServers = $value['scope'] === AccountManager::VISIBILITY_CONTACTS_ONLY || $value['scope'] === AccountManager::VISIBILITY_PUBLIC; $emptyValue = !isset($value['value']) || $value['value'] === ''; $noImage = $image === null; if ($shareWithTrustedServers && (!$emptyValue || !$noImage)) { $publish = true; switch ($property) { case AccountManager::PROPERTY_DISPLAYNAME: $vCard->add(new Text($vCard, 'FN', $value['value'])); $vCard->add(new Text($vCard, 'N', $this->splitFullName($value['value']))); break; case AccountManager::PROPERTY_AVATAR: if ($image !== null) { $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]); } break; case AccountManager::PROPERTY_EMAIL: $vCard->add(new Text($vCard, 'EMAIL', $value['value'], ['TYPE' => 'OTHER'])); break; case AccountManager::PROPERTY_WEBSITE: $vCard->add(new Text($vCard, 'URL', $value['value'])); break; case AccountManager::PROPERTY_PHONE: $vCard->add(new Text($vCard, 'TEL', $value['value'], ['TYPE' => 'OTHER'])); break; case AccountManager::PROPERTY_ADDRESS: $vCard->add(new Text($vCard, 'ADR', $value['value'], ['TYPE' => 'OTHER'])); break; case AccountManager::PROPERTY_TWITTER: $vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $value['value'], ['TYPE' => 'TWITTER'])); break; } } } if ($publish && !empty($cloudId)) { $vCard->add(new Text($vCard, 'CLOUD', $cloudId)); $vCard->validate(); return $vCard; } return null; } /** * @param string $fullName * @return string[] */ public function splitFullName($fullName) { // Very basic western style parsing. I'm not gonna implement // https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/NameSplitter.java ;) $elements = explode(' ', $fullName); $result = ['', '', '', '', '']; if (count($elements) > 2) { $result[0] = implode(' ', array_slice($elements, count($elements)-1)); $result[1] = $elements[0]; $result[2] = implode(' ', array_slice($elements, 1, count($elements)-2)); } elseif (count($elements) === 2) { $result[0] = $elements[1]; $result[1] = $elements[0]; } else { $result[0] = $elements[0]; } return $result; } /** * @param IUser $user * @return null|IImage */ private function getAvatarImage(IUser $user) { try { $image = $user->getAvatarImage(-1); return $image; } catch (\Exception $ex) { return null; } } } CardDAV/AddressBook.php 0000604 00000013561 15247164651 0010676 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OCA\DAV\DAV\Sharing\IShareable; use OCP\IL10N; use Sabre\CardDAV\Backend\BackendInterface; use Sabre\CardDAV\Card; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropPatch; /** * Class AddressBook * * @package OCA\DAV\CardDAV * @property BackendInterface|CardDavBackend $carddavBackend */ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { /** * AddressBook constructor. * * @param BackendInterface $carddavBackend * @param array $addressBookInfo * @param IL10N $l10n */ public function __construct(BackendInterface $carddavBackend, array $addressBookInfo, IL10N $l10n) { parent::__construct($carddavBackend, $addressBookInfo); if ($this->addressBookInfo['{DAV:}displayname'] === CardDavBackend::PERSONAL_ADDRESSBOOK_NAME && $this->getName() === CardDavBackend::PERSONAL_ADDRESSBOOK_URI) { $this->addressBookInfo['{DAV:}displayname'] = $l10n->t('Contacts'); } } /** * Updates the list of shares. * * The first array is a list of people that are to be added to the * addressbook. * * Every element in the add array has the following properties: * * href - A url. Usually a mailto: address * * commonName - Usually a first and last name, or false * * summary - A description of the share, can also be false * * readOnly - A boolean value * * Every element in the remove array is just the address string. * * @param array $add * @param array $remove * @return void * @throws Forbidden */ public function updateShares(array $add, array $remove) { if ($this->isShared()) { throw new Forbidden(); } $this->carddavBackend->updateShares($this, $add, $remove); } /** * Returns the list of people whom this addressbook is shared with. * * Every element in this array should have the following properties: * * href - Often a mailto: address * * commonName - Optional, for example a first + last name * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. * * readOnly - boolean * * summary - Optional, a description for the share * * @return array */ public function getShares() { if ($this->isShared()) { return []; } return $this->carddavBackend->getShares($this->getResourceId()); } public function getACL() { $acl = [ [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ]]; $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner(), 'protected' => true, ]; if ($this->getOwner() !== parent::getOwner()) { $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => parent::getOwner(), 'protected' => true, ]; if ($this->canWrite()) { $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => parent::getOwner(), 'protected' => true, ]; } } if ($this->getOwner() === 'principals/system/system') { $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => '{DAV:}authenticated', 'protected' => true, ]; } if ($this->isShared()) { return $acl; } return $this->carddavBackend->applyShareAcl($this->getResourceId(), $acl); } public function getChildACL() { return $this->getACL(); } public function getChild($name) { $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name); if (!$obj) { throw new NotFound('Card not found'); } $obj['acl'] = $this->getChildACL(); return new Card($this->carddavBackend, $this->addressBookInfo, $obj); } /** * @return int */ public function getResourceId() { return $this->addressBookInfo['id']; } public function getOwner() { if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) { return $this->addressBookInfo['{http://owncloud.org/ns}owner-principal']; } return parent::getOwner(); } public function delete() { if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) { $principal = 'principal:' . parent::getOwner(); $shares = $this->carddavBackend->getShares($this->getResourceId()); $shares = array_filter($shares, function($share) use ($principal){ return $share['href'] === $principal; }); if (empty($shares)) { throw new Forbidden(); } $this->carddavBackend->updateShares($this, [], [ 'href' => $principal ]); return; } parent::delete(); } public function propPatch(PropPatch $propPatch) { if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) { throw new Forbidden(); } parent::propPatch($propPatch); } public function getContactsGroups() { return $this->carddavBackend->collectCardProperties($this->getResourceId(), 'CATEGORIES'); } private function isShared() { if (!isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) { return false; } return $this->addressBookInfo['{http://owncloud.org/ns}owner-principal'] !== $this->addressBookInfo['principaluri']; } private function canWrite() { if (isset($this->addressBookInfo['{http://owncloud.org/ns}read-only'])) { return !$this->addressBookInfo['{http://owncloud.org/ns}read-only']; } return true; } } CardDAV/ContactsManager.php 0000604 00000004272 15247164651 0011546 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Georg Ehrke <georg@owncloud.com> * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OCP\Contacts\IManager; use OCP\IL10N; use OCP\IURLGenerator; class ContactsManager { /** @var CardDavBackend */ private $backend; /** @var IL10N */ private $l10n; /** * ContactsManager constructor. * * @param CardDavBackend $backend * @param IL10N $l10n */ public function __construct(CardDavBackend $backend, IL10N $l10n) { $this->backend = $backend; $this->l10n = $l10n; } /** * @param IManager $cm * @param string $userId * @param IURLGenerator $urlGenerator */ public function setupContactsProvider(IManager $cm, $userId, IURLGenerator $urlGenerator) { $addressBooks = $this->backend->getAddressBooksForUser("principals/users/$userId"); $this->register($cm, $addressBooks, $urlGenerator); $addressBooks = $this->backend->getAddressBooksForUser("principals/system/system"); $this->register($cm, $addressBooks, $urlGenerator); } /** * @param IManager $cm * @param $addressBooks * @param IURLGenerator $urlGenerator */ private function register(IManager $cm, $addressBooks, $urlGenerator) { foreach ($addressBooks as $addressBookInfo) { $addressBook = new \OCA\DAV\CardDAV\AddressBook($this->backend, $addressBookInfo, $this->l10n); $cm->registerAddressBook( new AddressBookImpl( $addressBook, $addressBookInfo, $this->backend, $urlGenerator ) ); } } } CardDAV/SyncJob.php 0000604 00000002236 15247164651 0010042 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OC\BackgroundJob\TimedJob; use OCA\DAV\AppInfo\Application; class SyncJob extends TimedJob { public function __construct() { // Run once a day $this->setInterval(24 * 60 * 60); } protected function run($argument) { $app = new Application(); /** @var SyncService $ss */ $ss = $app->getSyncService(); $ss->syncInstance(); } } CardDAV/UserAddressBooks.php 0000604 00000003716 15247164651 0011721 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OCP\IL10N; class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome { /** @var IL10N */ protected $l10n; /** * Returns a list of addressbooks * * @return array */ function getChildren() { if ($this->l10n === null) { $this->l10n = \OC::$server->getL10N('dav'); } $addressBooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri); $objects = []; foreach($addressBooks as $addressBook) { $objects[] = new AddressBook($this->carddavBackend, $addressBook, $this->l10n); } return $objects; } /** * Returns a list of ACE's for this node. * * Each ACE has the following properties: * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are * currently the only supported privileges * * 'principal', a url to the principal who owns the node * * 'protected' (optional), indicating that this ACE is not allowed to * be updated. * * @return array */ function getACL() { $acl = parent::getACL(); if ($this->principalUri === 'principals/system/system') { $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => '{DAV:}authenticated', 'protected' => true, ]; } return $acl; } } CardDAV/Xml/Groups.php 0000604 00000002351 15247164651 0010510 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV\Xml; use Sabre\Xml\XmlSerializable; use Sabre\Xml\Writer; class Groups implements XmlSerializable { const NS_OWNCLOUD = 'http://owncloud.org/ns'; /** @var string[] of TYPE:CHECKSUM */ private $groups; /** * @param string $groups */ public function __construct($groups) { $this->groups = $groups; } function xmlSerialize(Writer $writer) { foreach ($this->groups as $group) { $writer->writeElement('{' . self::NS_OWNCLOUD . '}group', $group); } } } CardDAV/AddressBookImpl.php 0000604 00000016403 15247164651 0011516 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Georg Ehrke <georg@owncloud.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\CardDAV; use OCP\Constants; use OCP\IAddressBook; use OCP\IURLGenerator; use Sabre\VObject\Component\VCard; use Sabre\VObject\Property; use Sabre\VObject\Reader; use Sabre\VObject\UUIDUtil; class AddressBookImpl implements IAddressBook { /** @var CardDavBackend */ private $backend; /** @var array */ private $addressBookInfo; /** @var AddressBook */ private $addressBook; /** @var IURLGenerator */ private $urlGenerator; /** * AddressBookImpl constructor. * * @param AddressBook $addressBook * @param array $addressBookInfo * @param CardDavBackend $backend * @param IUrlGenerator $urlGenerator */ public function __construct( AddressBook $addressBook, array $addressBookInfo, CardDavBackend $backend, IURLGenerator $urlGenerator) { $this->addressBook = $addressBook; $this->addressBookInfo = $addressBookInfo; $this->backend = $backend; $this->urlGenerator = $urlGenerator; } /** * @return string defining the technical unique key * @since 5.0.0 */ public function getKey() { return $this->addressBookInfo['id']; } /** * In comparison to getKey() this function returns a human readable (maybe translated) name * * @return mixed * @since 5.0.0 */ public function getDisplayName() { return $this->addressBookInfo['{DAV:}displayname']; } /** * @param string $pattern which should match within the $searchProperties * @param array $searchProperties defines the properties within the query pattern should match * @param array $options - for future use. One should always have options! * @return array an array of contacts which are arrays of key-value-pairs * @since 5.0.0 */ public function search($pattern, $searchProperties, $options) { $results = $this->backend->search($this->getKey(), $pattern, $searchProperties); $vCards = []; foreach ($results as $result) { $vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata'])); } return $vCards; } /** * @param array $properties this array if key-value-pairs defines a contact * @return array an array representing the contact just created or updated * @since 5.0.0 */ public function createOrUpdate($properties) { $update = false; if (!isset($properties['URI'])) { // create a new contact $uid = $this->createUid(); $uri = $uid . '.vcf'; $vCard = $this->createEmptyVCard($uid); } else { // update existing contact $uri = $properties['URI']; $vCardData = $this->backend->getCard($this->getKey(), $uri); $vCard = $this->readCard($vCardData['carddata']); $update = true; } foreach ($properties as $key => $value) { $vCard->$key = $vCard->createProperty($key, $value); } if ($update) { $this->backend->updateCard($this->getKey(), $uri, $vCard->serialize()); } else { $this->backend->createCard($this->getKey(), $uri, $vCard->serialize()); } return $this->vCard2Array($uri, $vCard); } /** * @return mixed * @since 5.0.0 */ public function getPermissions() { $permissions = $this->addressBook->getACL(); $result = 0; foreach ($permissions as $permission) { switch($permission['privilege']) { case '{DAV:}read': $result |= Constants::PERMISSION_READ; break; case '{DAV:}write': $result |= Constants::PERMISSION_CREATE; $result |= Constants::PERMISSION_UPDATE; break; case '{DAV:}all': $result |= Constants::PERMISSION_ALL; break; } } return $result; } /** * @param object $id the unique identifier to a contact * @return bool successful or not * @since 5.0.0 */ public function delete($id) { $uri = $this->backend->getCardUri($id); return $this->backend->deleteCard($this->addressBookInfo['id'], $uri); } /** * read vCard data into a vCard object * * @param string $cardData * @return VCard */ protected function readCard($cardData) { return Reader::read($cardData); } /** * create UID for contact * * @return string */ protected function createUid() { do { $uid = $this->getUid(); $contact = $this->backend->getContact($this->getKey(), $uid . '.vcf'); } while (!empty($contact)); return $uid; } /** * getUid is only there for testing, use createUid instead */ protected function getUid() { return UUIDUtil::getUUID(); } /** * create empty vcard * * @param string $uid * @return VCard */ protected function createEmptyVCard($uid) { $vCard = new VCard(); $vCard->UID = $uid; return $vCard; } /** * create array with all vCard properties * * @param string $uri * @param VCard $vCard * @return array */ protected function vCard2Array($uri, VCard $vCard) { $result = [ 'URI' => $uri, ]; foreach ($vCard->children() as $property) { if ($property->name === 'PHOTO' && $property->getValueType() === 'BINARY') { $url = $this->urlGenerator->getAbsoluteURL( $this->urlGenerator->linkTo('', 'remote.php') . '/dav/'); $url .= implode('/', [ 'addressbooks', substr($this->addressBookInfo['principaluri'], 11), //cut off 'principals/' $this->addressBookInfo['uri'], $uri ]) . '?photo'; $result['PHOTO'] = 'VALUE=uri:' . $url; } else if ($property->name === 'X-SOCIALPROFILE') { $type = $this->getTypeFromProperty($property); // Type is the social network, when it's empty we don't need this. if ($type !== null) { if (!isset($result[$property->name])) { $result[$property->name] = []; } $result[$property->name][$type] = $property->getValue(); } // The following properties can be set multiple times } else if (in_array($property->name, ['CLOUD', 'EMAIL', 'IMPP', 'TEL', 'URL'])) { if (!isset($result[$property->name])) { $result[$property->name] = []; } $result[$property->name][] = $property->getValue(); } else { $result[$property->name] = $property->getValue(); } } if ($this->addressBookInfo['principaluri'] === 'principals/system/system' && $this->addressBookInfo['uri'] === 'system') { $result['isLocalSystemBook'] = true; } return $result; } /** * Get the type of the current property * * @param Property $property * @return null|string */ protected function getTypeFromProperty(Property $property) { $parameters = $property->parameters(); // Type is the social network, when it's empty we don't need this. if (isset($parameters['TYPE'])) { /** @var \Sabre\VObject\Parameter $type */ $type = $parameters['TYPE']; return $type->getValue(); } return null; } } Command/CreateCalendar.php 0000604 00000005175 15247164651 0011507 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Command; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\Connector\Sabre\Principal; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class CreateCalendar extends Command { /** @var IUserManager */ protected $userManager; /** @var IGroupManager $groupManager */ private $groupManager; /** @var \OCP\IDBConnection */ protected $dbConnection; /** * @param IUserManager $userManager * @param IGroupManager $groupManager * @param IDBConnection $dbConnection */ function __construct(IUserManager $userManager, IGroupManager $groupManager, IDBConnection $dbConnection) { parent::__construct(); $this->userManager = $userManager; $this->groupManager = $groupManager; $this->dbConnection = $dbConnection; } protected function configure() { $this ->setName('dav:create-calendar') ->setDescription('Create a dav calendar') ->addArgument('user', InputArgument::REQUIRED, 'User for whom the calendar will be created') ->addArgument('name', InputArgument::REQUIRED, 'Name of the calendar'); } protected function execute(InputInterface $input, OutputInterface $output) { $user = $input->getArgument('user'); if (!$this->userManager->userExists($user)) { throw new \InvalidArgumentException("User <$user> in unknown."); } $principalBackend = new Principal( $this->userManager, $this->groupManager ); $random = \OC::$server->getSecureRandom(); $dispatcher = \OC::$server->getEventDispatcher(); $name = $input->getArgument('name'); $caldav = new CalDavBackend($this->dbConnection, $principalBackend, $this->userManager, $random, $dispatcher); $caldav->createCalendar("principals/users/$user", $name, []); } } Command/SyncBirthdayCalendar.php 0000604 00000005154 15247164651 0012704 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Command; use OCA\DAV\CalDAV\BirthdayService; use OCP\IUser; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class SyncBirthdayCalendar extends Command { /** @var BirthdayService */ private $birthdayService; /** @var IUserManager */ private $userManager; /** * @param IUserManager $userManager * @param BirthdayService $birthdayService */ function __construct(IUserManager $userManager, BirthdayService $birthdayService) { parent::__construct(); $this->birthdayService = $birthdayService; $this->userManager = $userManager; } protected function configure() { $this ->setName('dav:sync-birthday-calendar') ->setDescription('Synchronizes the birthday calendar') ->addArgument('user', InputArgument::OPTIONAL, 'User for whom the birthday calendar will be synchronized'); } /** * @param InputInterface $input * @param OutputInterface $output */ protected function execute(InputInterface $input, OutputInterface $output) { $user = $input->getArgument('user'); if (!is_null($user)) { if (!$this->userManager->userExists($user)) { throw new \InvalidArgumentException("User <$user> in unknown."); } $output->writeln("Start birthday calendar sync for $user"); $this->birthdayService->syncUser($user); return; } $output->writeln("Start birthday calendar sync for all users ..."); $p = new ProgressBar($output); $p->start(); $this->userManager->callForAllUsers(function($user) use ($p) { $p->advance(); /** @var IUser $user */ $this->birthdayService->syncUser($user->getUID()); }); $p->finish(); $output->writeln(''); } } Command/CreateAddressBook.php 0000604 00000004277 15247164651 0012200 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Björn Schießle <bjoern@schiessle.org> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Command; use OCA\DAV\CardDAV\CardDavBackend; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class CreateAddressBook extends Command { /** @var IUserManager */ private $userManager; /** @var CardDavBackend */ private $cardDavBackend; /** * @param IUserManager $userManager * @param CardDavBackend $cardDavBackend */ function __construct(IUserManager $userManager, CardDavBackend $cardDavBackend ) { parent::__construct(); $this->userManager = $userManager; $this->cardDavBackend = $cardDavBackend; } protected function configure() { $this ->setName('dav:create-addressbook') ->setDescription('Create a dav addressbook') ->addArgument('user', InputArgument::REQUIRED, 'User for whom the addressbook will be created') ->addArgument('name', InputArgument::REQUIRED, 'Name of the addressbook'); } protected function execute(InputInterface $input, OutputInterface $output) { $user = $input->getArgument('user'); if (!$this->userManager->userExists($user)) { throw new \InvalidArgumentException("User <$user> in unknown."); } $name = $input->getArgument('name'); $this->cardDavBackend->createAddressBook("principals/users/$user", $name, []); } } Command/SyncSystemAddressBook.php 0000604 00000003467 15247164651 0013116 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Command; use OCA\DAV\CardDAV\SyncService; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class SyncSystemAddressBook extends Command { /** @var SyncService */ private $syncService; /** * @param SyncService $syncService */ function __construct(SyncService $syncService) { parent::__construct(); $this->syncService = $syncService; } protected function configure() { $this ->setName('dav:sync-system-addressbook') ->setDescription('Synchronizes users to the system addressbook'); } /** * @param InputInterface $input * @param OutputInterface $output */ protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln('Syncing users ...'); $progress = new ProgressBar($output); $progress->start(); $this->syncService->syncInstance(function() use ($progress) { $progress->advance(); }); $progress->finish(); $output->writeln(''); } } Files/BrowserErrorPagePlugin.php 0000604 00000005453 15247164651 0012746 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Files; use OC\AppFramework\Http\Request; use OC_Template; use OCP\IRequest; use Sabre\DAV\Exception; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; class BrowserErrorPagePlugin extends ServerPlugin { /** @var Server */ private $server; /** * This initializes the plugin. * * This function is called by Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param Server $server * @return void */ function initialize(Server $server) { $this->server = $server; $server->on('exception', array($this, 'logException'), 1000); } /** * @param IRequest $request * @return bool */ public static function isBrowserRequest(IRequest $request) { if ($request->getMethod() !== 'GET') { return false; } return $request->isUserAgent([ Request::USER_AGENT_IE, Request::USER_AGENT_MS_EDGE, Request::USER_AGENT_CHROME, Request::USER_AGENT_FIREFOX, Request::USER_AGENT_SAFARI, ]); } /** * @param \Exception $ex */ public function logException(\Exception $ex) { if ($ex instanceof Exception) { $httpCode = $ex->getHTTPCode(); $headers = $ex->getHTTPHeaders($this->server); } else { $httpCode = 500; $headers = []; } $this->server->httpResponse->addHeaders($headers); $this->server->httpResponse->setStatus($httpCode); $body = $this->generateBody(); $this->server->httpResponse->setBody($body); $this->sendResponse(); } /** * @codeCoverageIgnore * @return bool|string */ public function generateBody() { $request = \OC::$server->getRequest(); $content = new OC_Template('dav', 'exception', 'guest'); $content->assign('title', $this->server->httpResponse->getStatusText()); $content->assign('remoteAddr', $request->getRemoteAddress()); $content->assign('requestID', $request->getId()); return $content->fetchPage(); } /** * @codeCoverageIgnore */ public function sendResponse() { $this->server->sapi->sendResponse($this->server->httpResponse); exit(); } } Files/FilesHome.php 0000604 00000003211 15247164651 0010176 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Files; use OCA\DAV\Connector\Sabre\Directory; use OCP\Files\FileInfo; use Sabre\DAV\Exception\Forbidden; use Sabre\HTTP\URLUtil; class FilesHome extends Directory { /** * @var array */ private $principalInfo; /** * FilesHome constructor. * * @param array $principalInfo */ public function __construct($principalInfo) { $this->principalInfo = $principalInfo; $view = \OC\Files\Filesystem::getView(); $rootInfo = $view->getFileInfo(''); if (!($rootInfo instanceof FileInfo)) { throw new \Exception('Home does not exist'); } parent::__construct($view, $rootInfo); } function delete() { throw new Forbidden('Permission denied to delete home folder'); } function getName() { list(,$name) = URLUtil::splitPath($this->principalInfo['uri']); return $name; } function setName($name) { throw new Forbidden('Permission denied to rename this folder'); } } Files/RootCollection.php 0000604 00000003505 15247164651 0011270 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Files; use Sabre\DAV\INode; use Sabre\DAVACL\AbstractPrincipalCollection; use Sabre\HTTP\URLUtil; use Sabre\DAV\SimpleCollection; class RootCollection extends AbstractPrincipalCollection { /** * This method returns a node for a principal. * * The passed array contains principal information, and is guaranteed to * at least contain a uri item. Other properties may or may not be * supplied by the authentication backend. * * @param array $principalInfo * @return INode */ function getChildForPrincipal(array $principalInfo) { list(,$name) = URLUtil::splitPath($principalInfo['uri']); $user = \OC::$server->getUserSession()->getUser(); if (is_null($user) || $name !== $user->getUID()) { // a user is only allowed to see their own home contents, so in case another collection // is accessed, we return a simple empty collection for now // in the future this could be considered to be used for accessing shared files return new SimpleCollection($name); } return new FilesHome($principalInfo); } function getName() { return 'files'; } } Files/Sharing/FilesDropPlugin.php 0000604 00000004165 15247164651 0012775 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\Files\Sharing; use OC\Files\View; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\DAV\ServerPlugin; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; /** * Make sure that the destination is writable */ class FilesDropPlugin extends ServerPlugin { /** @var View */ private $view; /** @var bool */ private $enabled = false; /** * @param View $view */ public function setView($view) { $this->view = $view; } public function enable() { $this->enabled = true; } /** * This initializes the plugin. * * @param \Sabre\DAV\Server $server Sabre server * * @return void * @throws MethodNotAllowed */ public function initialize(\Sabre\DAV\Server $server) { $server->on('beforeMethod', [$this, 'beforeMethod'], 999); $this->enabled = false; } public function beforeMethod(RequestInterface $request, ResponseInterface $response){ if (!$this->enabled) { return; } if ($request->getMethod() !== 'PUT') { throw new MethodNotAllowed('Only PUT is allowed on files drop'); } $path = explode('/', $request->getPath()); $path = array_pop($path); $newName = \OC_Helper::buildNotExistingFileNameForView('/', $path, $this->view); $url = $request->getBaseUrl() . $newName; $request->setUrl($url); } } Files/Sharing/PublicLinkCheckPlugin.php 0000604 00000003241 15247164651 0014072 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Robin Appelman <robin@icewind.nl> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\Files\Sharing; use OCP\Files\FileInfo; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\ServerPlugin; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; /** * Verify that the public link share is valid */ class PublicLinkCheckPlugin extends ServerPlugin { /** * @var FileInfo */ private $fileInfo; /** * @param FileInfo $fileInfo */ public function setFileInfo($fileInfo) { $this->fileInfo = $fileInfo; } /** * This initializes the plugin. * * @param \Sabre\DAV\Server $server Sabre server * * @return void */ public function initialize(\Sabre\DAV\Server $server) { $server->on('beforeMethod', [$this, 'beforeMethod']); } public function beforeMethod(RequestInterface $request, ResponseInterface $response){ // verify that the owner didn't have his share permissions revoked if ($this->fileInfo && !$this->fileInfo->isShareable()) { throw new NotFound(); } } } Files/FileSearchBackend.php 0000604 00000023460 15247164651 0011610 0 ustar 00 <?php /** * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ namespace OCA\DAV\Files; use OC\Files\Search\SearchBinaryOperator; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchOrder; use OC\Files\Search\SearchQuery; use OC\Files\View; use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\TagsPlugin; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchOrder; use OCP\Files\Search\ISearchQuery; use OCP\IUser; use OCP\Share\IManager; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Tree; use SearchDAV\Backend\ISearchBackend; use SearchDAV\Backend\SearchPropertyDefinition; use SearchDAV\Backend\SearchResult; use SearchDAV\XML\BasicSearch; use SearchDAV\XML\Literal; use SearchDAV\XML\Operator; use SearchDAV\XML\Order; class FileSearchBackend implements ISearchBackend { /** @var Tree */ private $tree; /** @var IUser */ private $user; /** @var IRootFolder */ private $rootFolder; /** @var IManager */ private $shareManager; /** @var View */ private $view; /** * FileSearchBackend constructor. * * @param Tree $tree * @param IUser $user * @param IRootFolder $rootFolder * @param IManager $shareManager * @param View $view * @internal param IRootFolder $rootFolder */ public function __construct(Tree $tree, IUser $user, IRootFolder $rootFolder, IManager $shareManager, View $view) { $this->tree = $tree; $this->user = $user; $this->rootFolder = $rootFolder; $this->shareManager = $shareManager; $this->view = $view; } /** * Search endpoint will be remote.php/dav * * @return string */ public function getArbiterPath() { return ''; } public function isValidScope($href, $depth, $path) { // only allow scopes inside the dav server if (is_null($path)) { return false; } try { $node = $this->tree->getNodeForPath($path); return $node instanceof Directory; } catch (NotFound $e) { return false; } } public function getPropertyDefinitionsForScope($href, $path) { // all valid scopes support the same schema //todo dynamically load all propfind properties that are supported return [ // queryable properties new SearchPropertyDefinition('{DAV:}displayname', true, false, true), new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true), new SearchPropertyDefinition('{DAV:}getlastmodified', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME), new SearchPropertyDefinition(FilesPlugin::SIZE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), new SearchPropertyDefinition(TagsPlugin::FAVORITE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_BOOLEAN), new SearchPropertyDefinition(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, true, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), // select only properties new SearchPropertyDefinition('{DAV:}resourcetype', false, true, false), new SearchPropertyDefinition('{DAV:}getcontentlength', false, true, false), new SearchPropertyDefinition(FilesPlugin::CHECKSUMS_PROPERTYNAME, false, true, false), new SearchPropertyDefinition(FilesPlugin::PERMISSIONS_PROPERTYNAME, false, true, false), new SearchPropertyDefinition(FilesPlugin::GETETAG_PROPERTYNAME, false, true, false), new SearchPropertyDefinition(FilesPlugin::OWNER_ID_PROPERTYNAME, false, true, false), new SearchPropertyDefinition(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, false, true, false), new SearchPropertyDefinition(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, false, true, false), new SearchPropertyDefinition(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_BOOLEAN), new SearchPropertyDefinition(FilesPlugin::FILEID_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), ]; } /** * @param BasicSearch $search * @return SearchResult[] */ public function search(BasicSearch $search) { if (count($search->from) !== 1) { throw new \InvalidArgumentException('Searching more than one folder is not supported'); } $query = $this->transformQuery($search); $scope = $search->from[0]; if ($scope->path === null) { throw new \InvalidArgumentException('Using uri\'s as scope is not supported, please use a path relative to the search arbiter instead'); } $node = $this->tree->getNodeForPath($scope->path); if (!$node instanceof Directory) { throw new \InvalidArgumentException('Search is only supported on directories'); } $fileInfo = $node->getFileInfo(); $folder = $this->rootFolder->get($fileInfo->getPath()); /** @var Folder $folder $results */ $results = $folder->search($query); return array_map(function (Node $node) { if ($node instanceof Folder) { return new SearchResult(new \OCA\DAV\Connector\Sabre\Directory($this->view, $node, $this->tree, $this->shareManager), $this->getHrefForNode($node)); } else { return new SearchResult(new \OCA\DAV\Connector\Sabre\File($this->view, $node, $this->shareManager), $this->getHrefForNode($node)); } }, $results); } /** * @param Node $node * @return string */ private function getHrefForNode(Node $node) { $base = '/files/' . $this->user->getUID(); return $base . $this->view->getRelativePath($node->getPath()); } /** * @param BasicSearch $query * @return ISearchQuery */ private function transformQuery(BasicSearch $query) { // TODO offset, limit $orders = array_map([$this, 'mapSearchOrder'], $query->orderBy); return new SearchQuery($this->transformSearchOperation($query->where), 0, 0, $orders, $this->user); } /** * @param Order $order * @return ISearchOrder */ private function mapSearchOrder(Order $order) { return new SearchOrder($order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING, $this->mapPropertyNameToColumn($order->property)); } /** * @param Operator $operator * @return ISearchOperator */ private function transformSearchOperation(Operator $operator) { list(, $trimmedType) = explode('}', $operator->type); switch ($operator->type) { case Operator::OPERATION_AND: case Operator::OPERATION_OR: case Operator::OPERATION_NOT: $arguments = array_map([$this, 'transformSearchOperation'], $operator->arguments); return new SearchBinaryOperator($trimmedType, $arguments); case Operator::OPERATION_EQUAL: case Operator::OPERATION_GREATER_OR_EQUAL_THAN: case Operator::OPERATION_GREATER_THAN: case Operator::OPERATION_LESS_OR_EQUAL_THAN: case Operator::OPERATION_LESS_THAN: case Operator::OPERATION_IS_LIKE: if (count($operator->arguments) !== 2) { throw new \InvalidArgumentException('Invalid number of arguments for ' . $trimmedType . ' operation'); } if (!is_string($operator->arguments[0])) { throw new \InvalidArgumentException('Invalid argument 1 for ' . $trimmedType . ' operation, expected property'); } if (!($operator->arguments[1] instanceof Literal)) { throw new \InvalidArgumentException('Invalid argument 2 for ' . $trimmedType . ' operation, expected literal'); } return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($operator->arguments[0]), $this->castValue($operator->arguments[0], $operator->arguments[1]->value)); case Operator::OPERATION_IS_COLLECTION: return new SearchComparison('eq', 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE); default: throw new \InvalidArgumentException('Unsupported operation ' . $trimmedType . ' (' . $operator->type . ')'); } } /** * @param string $propertyName * @return string */ private function mapPropertyNameToColumn($propertyName) { switch ($propertyName) { case '{DAV:}displayname': return 'name'; case '{DAV:}getcontenttype': return 'mimetype'; case '{DAV:}getlastmodified': return 'mtime'; case FilesPlugin::SIZE_PROPERTYNAME: return 'size'; case TagsPlugin::FAVORITE_PROPERTYNAME: return 'favorite'; case TagsPlugin::TAGS_PROPERTYNAME: return 'tagname'; case FilesPlugin::INTERNAL_FILEID_PROPERTYNAME: return 'fileid'; default: throw new \InvalidArgumentException('Unsupported property for search or order: ' . $propertyName); } } private function castValue($propertyName, $value) { $allProps = $this->getPropertyDefinitionsForScope('', ''); foreach ($allProps as $prop) { if ($prop->name === $propertyName) { $dataType = $prop->dataType; switch ($dataType) { case SearchPropertyDefinition::DATATYPE_BOOLEAN: return $value === 'yes'; case SearchPropertyDefinition::DATATYPE_DECIMAL: case SearchPropertyDefinition::DATATYPE_INTEGER: case SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER: return 0 + $value; case SearchPropertyDefinition::DATATYPE_DATETIME: if (is_numeric($value)) { return 0 + $value; } $date = \DateTime::createFromFormat(\DateTime::ATOM, $value); return ($date instanceof \DateTime) ? $date->getTimestamp() : 0; default: return $value; } } } return $value; } } SystemTag/SystemTagsObjectTypeCollection.php 0000604 00000007562 15247164651 0015326 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\SystemTag; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\ICollection; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; use OCP\IUserSession; use OCP\IGroupManager; /** * Collection containing object ids by object type */ class SystemTagsObjectTypeCollection implements ICollection { /** * @var string */ private $objectType; /** * @var ISystemTagManager */ private $tagManager; /** * @var ISystemTagObjectMapper */ private $tagMapper; /** * @var IGroupManager */ private $groupManager; /** * @var IUserSession */ private $userSession; /** * @var \Closure **/ protected $childExistsFunction; /** * Constructor * * @param string $objectType object type * @param ISystemTagManager $tagManager * @param ISystemTagObjectMapper $tagMapper * @param IUserSession $userSession * @param IGroupManager $groupManager * @param \Closure $childExistsFunction */ public function __construct( $objectType, ISystemTagManager $tagManager, ISystemTagObjectMapper $tagMapper, IUserSession $userSession, IGroupManager $groupManager, \Closure $childExistsFunction ) { $this->tagManager = $tagManager; $this->tagMapper = $tagMapper; $this->objectType = $objectType; $this->userSession = $userSession; $this->groupManager = $groupManager; $this->childExistsFunction = $childExistsFunction; } /** * @param string $name * @param resource|string $data Initial payload * @return null|string * @throws Forbidden */ function createFile($name, $data = null) { throw new Forbidden('Permission denied to create nodes'); } /** * @param string $name * @throws Forbidden */ function createDirectory($name) { throw new Forbidden('Permission denied to create collections'); } /** * @param string $objectId * @return SystemTagsObjectMappingCollection * @throws NotFound */ function getChild($objectId) { // make sure the object exists and is reachable if(!$this->childExists($objectId)) { throw new NotFound('Entity does not exist or is not available'); } return new SystemTagsObjectMappingCollection( $objectId, $this->objectType, $this->userSession->getUser(), $this->tagManager, $this->tagMapper ); } function getChildren() { // do not list object ids throw new MethodNotAllowed(); } /** * Checks if a child-node with the specified name exists * * @param string $name * @return bool */ function childExists($name) { return call_user_func($this->childExistsFunction, $name); } function delete() { throw new Forbidden('Permission denied to delete this collection'); } function getName() { return $this->objectType; } /** * @param string $name * @throws Forbidden */ function setName($name) { throw new Forbidden('Permission denied to rename this collection'); } /** * Returns the last modification time, as a unix timestamp * * @return int */ function getLastModified() { return null; } } SystemTag/SystemTagsRelationsCollection.php 0000604 00000005141 15247164651 0015205 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Joas Schilling <coding@schilljs.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\SystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; use OCP\SystemTag\SystemTagsEntityEvent; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\SimpleCollection; use OCP\IUserSession; use OCP\IGroupManager; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class SystemTagsRelationsCollection extends SimpleCollection { /** * SystemTagsRelationsCollection constructor. * * @param ISystemTagManager $tagManager * @param ISystemTagObjectMapper $tagMapper * @param IUserSession $userSession * @param IGroupManager $groupManager * @param EventDispatcherInterface $dispatcher */ public function __construct( ISystemTagManager $tagManager, ISystemTagObjectMapper $tagMapper, IUserSession $userSession, IGroupManager $groupManager, EventDispatcherInterface $dispatcher ) { $children = [ new SystemTagsObjectTypeCollection( 'files', $tagManager, $tagMapper, $userSession, $groupManager, function($name) { $nodes = \OC::$server->getUserFolder()->getById(intval($name)); return !empty($nodes); } ), ]; $event = new SystemTagsEntityEvent(SystemTagsEntityEvent::EVENT_ENTITY); $dispatcher->dispatch(SystemTagsEntityEvent::EVENT_ENTITY, $event); foreach ($event->getEntityCollections() as $entity => $entityExistsFunction) { $children[] = new SystemTagsObjectTypeCollection( $entity, $tagManager, $tagMapper, $userSession, $groupManager, $entityExistsFunction ); } parent::__construct('root', $children); } function getName() { return 'systemtags-relations'; } function setName($name) { throw new Forbidden('Permission denied to rename this collection'); } } SystemTag/SystemTagNode.php 0000604 00000011062 15247164651 0011732 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\SystemTag; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\DAV\Exception\Conflict; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\TagNotFoundException; use OCP\SystemTag\TagAlreadyExistsException; use OCP\IUser; /** * DAV node representing a system tag, with the name being the tag id. */ class SystemTagNode implements \Sabre\DAV\INode { /** * @var ISystemTag */ protected $tag; /** * @var ISystemTagManager */ protected $tagManager; /** * User * * @var IUser */ protected $user; /** * Whether to allow permissions for admins * * @var bool */ protected $isAdmin; /** * Sets up the node, expects a full path name * * @param ISystemTag $tag system tag * @param IUser $user user * @param bool $isAdmin whether to allow operations for admins * @param ISystemTagManager $tagManager tag manager */ public function __construct(ISystemTag $tag, IUser $user, $isAdmin, ISystemTagManager $tagManager) { $this->tag = $tag; $this->user = $user; $this->isAdmin = $isAdmin; $this->tagManager = $tagManager; } /** * Returns the id of the tag * * @return string */ public function getName() { return $this->tag->getId(); } /** * Returns the system tag represented by this node * * @return ISystemTag system tag */ public function getSystemTag() { return $this->tag; } /** * Renames the node * * @param string $name The new name * * @throws MethodNotAllowed not allowed to rename node */ public function setName($name) { throw new MethodNotAllowed(); } /** * Update tag * * @param string $name new tag name * @param bool $userVisible user visible * @param bool $userAssignable user assignable * @throws NotFound whenever the given tag id does not exist * @throws Forbidden whenever there is no permission to update said tag * @throws Conflict whenever a tag already exists with the given attributes */ public function update($name, $userVisible, $userAssignable) { try { if (!$this->tagManager->canUserSeeTag($this->tag, $this->user)) { throw new NotFound('Tag with id ' . $this->tag->getId() . ' does not exist'); } if (!$this->tagManager->canUserAssignTag($this->tag, $this->user)) { throw new Forbidden('No permission to update tag ' . $this->tag->getId()); } // only admin is able to change permissions, regular users can only rename if (!$this->isAdmin) { // only renaming is allowed for regular users if ($userVisible !== $this->tag->isUserVisible() || $userAssignable !== $this->tag->isUserAssignable() ) { throw new Forbidden('No permission to update permissions for tag ' . $this->tag->getId()); } } $this->tagManager->updateTag($this->tag->getId(), $name, $userVisible, $userAssignable); } catch (TagNotFoundException $e) { throw new NotFound('Tag with id ' . $this->tag->getId() . ' does not exist'); } catch (TagAlreadyExistsException $e) { throw new Conflict( 'Tag with the properties "' . $name . '", ' . $userVisible . ', ' . $userAssignable . ' already exists' ); } } /** * Returns null, not supported * */ public function getLastModified() { return null; } public function delete() { try { if (!$this->isAdmin) { throw new Forbidden('No permission to delete tag ' . $this->tag->getId()); } if (!$this->tagManager->canUserSeeTag($this->tag, $this->user)) { throw new NotFound('Tag with id ' . $this->tag->getId() . ' not found'); } $this->tagManager->deleteTags($this->tag->getId()); } catch (TagNotFoundException $e) { // can happen if concurrent deletion occurred throw new NotFound('Tag with id ' . $this->tag->getId() . ' not found', 0, $e); } } } SystemTag/SystemTagPlugin.php 0000604 00000022347 15247164651 0012313 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Lukas Reschke <lukas@statuscode.ch> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\SystemTag; use OCP\IGroupManager; use OCP\IUserSession; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\Conflict; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\UnsupportedMediaType; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\TagAlreadyExistsException; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; /** * Sabre plugin to handle system tags: * * - makes it possible to create new tags with POST operation * - get/set Webdav properties for tags * */ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin { // namespace const NS_OWNCLOUD = 'http://owncloud.org/ns'; const ID_PROPERTYNAME = '{http://owncloud.org/ns}id'; const DISPLAYNAME_PROPERTYNAME = '{http://owncloud.org/ns}display-name'; const USERVISIBLE_PROPERTYNAME = '{http://owncloud.org/ns}user-visible'; const USERASSIGNABLE_PROPERTYNAME = '{http://owncloud.org/ns}user-assignable'; const GROUPS_PROPERTYNAME = '{http://owncloud.org/ns}groups'; const CANASSIGN_PROPERTYNAME = '{http://owncloud.org/ns}can-assign'; /** * @var \Sabre\DAV\Server $server */ private $server; /** * @var ISystemTagManager */ protected $tagManager; /** * @var IUserSession */ protected $userSession; /** * @var IGroupManager */ protected $groupManager; /** * @param ISystemTagManager $tagManager tag manager * @param IGroupManager $groupManager * @param IUserSession $userSession */ public function __construct(ISystemTagManager $tagManager, IGroupManager $groupManager, IUserSession $userSession) { $this->tagManager = $tagManager; $this->userSession = $userSession; $this->groupManager = $groupManager; } /** * This initializes the plugin. * * This function is called by \Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param \Sabre\DAV\Server $server * @return void */ public function initialize(\Sabre\DAV\Server $server) { $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; $server->protectedProperties[] = self::ID_PROPERTYNAME; $server->on('propFind', array($this, 'handleGetProperties')); $server->on('propPatch', array($this, 'handleUpdateProperties')); $server->on('method:POST', [$this, 'httpPost']); $this->server = $server; } /** * POST operation on system tag collections * * @param RequestInterface $request request object * @param ResponseInterface $response response object * @return null|false */ public function httpPost(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Making sure the node exists $node = $this->server->tree->getNodeForPath($path); if ($node instanceof SystemTagsByIdCollection || $node instanceof SystemTagsObjectMappingCollection) { $data = $request->getBodyAsString(); $tag = $this->createTag($data, $request->getHeader('Content-Type')); if ($node instanceof SystemTagsObjectMappingCollection) { // also add to collection $node->createFile($tag->getId()); $url = $request->getBaseUrl() . 'systemtags/'; } else { $url = $request->getUrl(); } if ($url[strlen($url) - 1] !== '/') { $url .= '/'; } $response->setHeader('Content-Location', $url . $tag->getId()); // created $response->setStatus(201); return false; } } /** * Creates a new tag * * @param string $data JSON encoded string containing the properties of the tag to create * @param string $contentType content type of the data * @return ISystemTag newly created system tag * * @throws BadRequest if a field was missing * @throws Conflict if a tag with the same properties already exists * @throws UnsupportedMediaType if the content type is not supported */ private function createTag($data, $contentType = 'application/json') { if (explode(';', $contentType)[0] === 'application/json') { $data = json_decode($data, true); } else { throw new UnsupportedMediaType(); } if (!isset($data['name'])) { throw new BadRequest('Missing "name" attribute'); } $tagName = $data['name']; $userVisible = true; $userAssignable = true; if (isset($data['userVisible'])) { $userVisible = (bool)$data['userVisible']; } if (isset($data['userAssignable'])) { $userAssignable = (bool)$data['userAssignable']; } $groups = []; if (isset($data['groups'])) { $groups = $data['groups']; if (is_string($groups)) { $groups = explode('|', $groups); } } if($userVisible === false || $userAssignable === false || !empty($groups)) { if(!$this->userSession->isLoggedIn() || !$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) { throw new BadRequest('Not sufficient permissions'); } } try { $tag = $this->tagManager->createTag($tagName, $userVisible, $userAssignable); if (!empty($groups)) { $this->tagManager->setTagGroups($tag, $groups); } return $tag; } catch (TagAlreadyExistsException $e) { throw new Conflict('Tag already exists', 0, $e); } } /** * Retrieves system tag properties * * @param PropFind $propFind * @param \Sabre\DAV\INode $node */ public function handleGetProperties( PropFind $propFind, \Sabre\DAV\INode $node ) { if (!($node instanceof SystemTagNode) && !($node instanceof SystemTagMappingNode)) { return; } $propFind->handle(self::ID_PROPERTYNAME, function() use ($node) { return $node->getSystemTag()->getId(); }); $propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function() use ($node) { return $node->getSystemTag()->getName(); }); $propFind->handle(self::USERVISIBLE_PROPERTYNAME, function() use ($node) { return $node->getSystemTag()->isUserVisible() ? 'true' : 'false'; }); $propFind->handle(self::USERASSIGNABLE_PROPERTYNAME, function() use ($node) { // this is the tag's inherent property "is user assignable" return $node->getSystemTag()->isUserAssignable() ? 'true' : 'false'; }); $propFind->handle(self::CANASSIGN_PROPERTYNAME, function() use ($node) { // this is the effective permission for the current user return $this->tagManager->canUserAssignTag($node->getSystemTag(), $this->userSession->getUser()) ? 'true' : 'false'; }); $propFind->handle(self::GROUPS_PROPERTYNAME, function() use ($node) { if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) { // property only available for admins throw new Forbidden(); } $groups = []; // no need to retrieve groups for namespaces that don't qualify if ($node->getSystemTag()->isUserVisible() && !$node->getSystemTag()->isUserAssignable()) { $groups = $this->tagManager->getTagGroups($node->getSystemTag()); } return implode('|', $groups); }); } /** * Updates tag attributes * * @param string $path * @param PropPatch $propPatch * * @return void */ public function handleUpdateProperties($path, PropPatch $propPatch) { $node = $this->server->tree->getNodeForPath($path); if (!($node instanceof SystemTagNode)) { return; } $propPatch->handle([ self::DISPLAYNAME_PROPERTYNAME, self::USERVISIBLE_PROPERTYNAME, self::USERASSIGNABLE_PROPERTYNAME, self::GROUPS_PROPERTYNAME, ], function($props) use ($node) { $tag = $node->getSystemTag(); $name = $tag->getName(); $userVisible = $tag->isUserVisible(); $userAssignable = $tag->isUserAssignable(); $updateTag = false; if (isset($props[self::DISPLAYNAME_PROPERTYNAME])) { $name = $props[self::DISPLAYNAME_PROPERTYNAME]; $updateTag = true; } if (isset($props[self::USERVISIBLE_PROPERTYNAME])) { $propValue = $props[self::USERVISIBLE_PROPERTYNAME]; $userVisible = ($propValue !== 'false' && $propValue !== '0'); $updateTag = true; } if (isset($props[self::USERASSIGNABLE_PROPERTYNAME])) { $propValue = $props[self::USERASSIGNABLE_PROPERTYNAME]; $userAssignable = ($propValue !== 'false' && $propValue !== '0'); $updateTag = true; } if (isset($props[self::GROUPS_PROPERTYNAME])) { if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) { // property only available for admins throw new Forbidden(); } $propValue = $props[self::GROUPS_PROPERTYNAME]; $groupIds = explode('|', $propValue); $this->tagManager->setTagGroups($tag, $groupIds); } if ($updateTag) { $node->update($name, $userVisible, $userAssignable); } return true; }); } } SystemTag/SystemTagsObjectMappingCollection.php 0000604 00000012077 15247164651 0015775 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\SystemTag; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\PreconditionFailed; use Sabre\DAV\ICollection; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\TagNotFoundException; use OCP\IUser; /** * Collection containing tags by object id */ class SystemTagsObjectMappingCollection implements ICollection { /** * @var string */ private $objectId; /** * @var string */ private $objectType; /** * @var ISystemTagManager */ private $tagManager; /** * @var ISystemTagObjectMapper */ private $tagMapper; /** * User * * @var IUser */ private $user; /** * Constructor * * @param string $objectId object id * @param string $objectType object type * @param IUser $user user * @param ISystemTagManager $tagManager tag manager * @param ISystemTagObjectMapper $tagMapper tag mapper */ public function __construct( $objectId, $objectType, IUser $user, ISystemTagManager $tagManager, ISystemTagObjectMapper $tagMapper ) { $this->tagManager = $tagManager; $this->tagMapper = $tagMapper; $this->objectId = $objectId; $this->objectType = $objectType; $this->user = $user; } function createFile($tagId, $data = null) { try { $tags = $this->tagManager->getTagsByIds([$tagId]); $tag = current($tags); if (!$this->tagManager->canUserSeeTag($tag, $this->user)) { throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign'); } if (!$this->tagManager->canUserAssignTag($tag, $this->user)) { throw new Forbidden('No permission to assign tag ' . $tagId); } $this->tagMapper->assignTags($this->objectId, $this->objectType, $tagId); } catch (TagNotFoundException $e) { throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign'); } } function createDirectory($name) { throw new Forbidden('Permission denied to create collections'); } function getChild($tagId) { try { if ($this->tagMapper->haveTag([$this->objectId], $this->objectType, $tagId, true)) { $tag = $this->tagManager->getTagsByIds([$tagId]); $tag = current($tag); if ($this->tagManager->canUserSeeTag($tag, $this->user)) { return $this->makeNode($tag); } } throw new NotFound('Tag with id ' . $tagId . ' not present for object ' . $this->objectId); } catch (\InvalidArgumentException $e) { throw new BadRequest('Invalid tag id', 0, $e); } catch (TagNotFoundException $e) { throw new NotFound('Tag with id ' . $tagId . ' not found', 0, $e); } } function getChildren() { $tagIds = current($this->tagMapper->getTagIdsForObjects([$this->objectId], $this->objectType)); if (empty($tagIds)) { return []; } $tags = $this->tagManager->getTagsByIds($tagIds); // filter out non-visible tags $tags = array_filter($tags, function($tag) { return $this->tagManager->canUserSeeTag($tag, $this->user); }); return array_values(array_map(function($tag) { return $this->makeNode($tag); }, $tags)); } function childExists($tagId) { try { $result = ($this->tagMapper->haveTag([$this->objectId], $this->objectType, $tagId, true)); if ($result) { $tags = $this->tagManager->getTagsByIds([$tagId]); $tag = current($tags); if (!$this->tagManager->canUserSeeTag($tag, $this->user)) { return false; } } return $result; } catch (\InvalidArgumentException $e) { throw new BadRequest('Invalid tag id', 0, $e); } catch (TagNotFoundException $e) { return false; } } function delete() { throw new Forbidden('Permission denied to delete this collection'); } function getName() { return $this->objectId; } function setName($name) { throw new Forbidden('Permission denied to rename this collection'); } /** * Returns the last modification time, as a unix timestamp * * @return int */ function getLastModified() { return null; } /** * Create a sabre node for the mapping of the * given system tag to the collection's object * * @param ISystemTag $tag * * @return SystemTagMappingNode */ private function makeNode(ISystemTag $tag) { return new SystemTagMappingNode( $tag, $this->objectId, $this->objectType, $this->user, $this->tagManager, $this->tagMapper ); } } SystemTag/SystemTagsByIdCollection.php 0000604 00000010311 15247164651 0014067 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\SystemTag; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\ICollection; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\TagNotFoundException; use OCP\IGroupManager; use OCP\IUserSession; class SystemTagsByIdCollection implements ICollection { /** * @var ISystemTagManager */ private $tagManager; /** * @var IGroupManager */ private $groupManager; /** * @var IUserSession */ private $userSession; /** * SystemTagsByIdCollection constructor. * * @param ISystemTagManager $tagManager * @param IUserSession $userSession * @param IGroupManager $groupManager */ public function __construct( ISystemTagManager $tagManager, IUserSession $userSession, IGroupManager $groupManager ) { $this->tagManager = $tagManager; $this->userSession = $userSession; $this->groupManager = $groupManager; } /** * Returns whether the currently logged in user is an administrator * * @return bool true if the user is an admin */ private function isAdmin() { $user = $this->userSession->getUser(); if ($user !== null) { return $this->groupManager->isAdmin($user->getUID()); } return false; } /** * @param string $name * @param resource|string $data Initial payload * @throws Forbidden */ function createFile($name, $data = null) { throw new Forbidden('Cannot create tags by id'); } /** * @param string $name */ function createDirectory($name) { throw new Forbidden('Permission denied to create collections'); } /** * @param string $name */ function getChild($name) { try { $tag = $this->tagManager->getTagsByIds([$name]); $tag = current($tag); if (!$this->tagManager->canUserSeeTag($tag, $this->userSession->getUser())) { throw new NotFound('Tag with id ' . $name . ' not found'); } return $this->makeNode($tag); } catch (\InvalidArgumentException $e) { throw new BadRequest('Invalid tag id', 0, $e); } catch (TagNotFoundException $e) { throw new NotFound('Tag with id ' . $name . ' not found', 0, $e); } } function getChildren() { $visibilityFilter = true; if ($this->isAdmin()) { $visibilityFilter = null; } $tags = $this->tagManager->getAllTags($visibilityFilter); return array_map(function($tag) { return $this->makeNode($tag); }, $tags); } /** * @param string $name */ function childExists($name) { try { $tag = $this->tagManager->getTagsByIds([$name]); $tag = current($tag); if (!$this->tagManager->canUserSeeTag($tag, $this->userSession->getUser())) { return false; } return true; } catch (\InvalidArgumentException $e) { throw new BadRequest('Invalid tag id', 0, $e); } catch (TagNotFoundException $e) { return false; } } function delete() { throw new Forbidden('Permission denied to delete this collection'); } function getName() { return 'systemtags'; } function setName($name) { throw new Forbidden('Permission denied to rename this collection'); } /** * Returns the last modification time, as a unix timestamp * * @return int */ function getLastModified() { return null; } /** * Create a sabre node for the given system tag * * @param ISystemTag $tag * * @return SystemTagNode */ private function makeNode(ISystemTag $tag) { return new SystemTagNode($tag, $this->userSession->getUser(), $this->isAdmin(), $this->tagManager); } } SystemTag/SystemTagMappingNode.php 0000604 00000007227 15247164651 0013256 0 ustar 00 <?php /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> * * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see <http://www.gnu.org/licenses/> * */ namespace OCA\DAV\SystemTag; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\MethodNotAllowed; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; use OCP\SystemTag\TagNotFoundException; use OCP\IUser; /** * Mapping node for system tag to object id */ class SystemTagMappingNode implements \Sabre\DAV\INode { /** * @var ISystemTag */ protected $tag; /** * @var string */ private $objectId; /** * @var string */ private $objectType; /** * User * * @var IUser */ protected $user; /** * @var ISystemTagManager */ protected $tagManager; /** * @var ISystemTagObjectMapper */ private $tagMapper; /** * Sets up the node, expects a full path name * * @param ISystemTag $tag system tag * @param string $objectId * @param string $objectType * @param IUser $user user * @param ISystemTagManager $tagManager * @param ISystemTagObjectMapper $tagMapper */ public function __construct( ISystemTag $tag, $objectId, $objectType, IUser $user, ISystemTagManager $tagManager, ISystemTagObjectMapper $tagMapper ) { $this->tag = $tag; $this->objectId = $objectId; $this->objectType = $objectType; $this->user = $user; $this->tagManager = $tagManager; $this->tagMapper = $tagMapper; } /** * Returns the object id of the relationship * * @return string object id */ public function getObjectId() { return $this->objectId; } /** * Returns the object type of the relationship * * @return string object type */ public function getObjectType() { return $this->objectType; } /** * Returns the system tag represented by this node * * @return ISystemTag system tag */ public function getSystemTag() { return $this->tag; } /** * Returns the id of the tag * * @return string */ public function getName() { return $this->tag->getId(); } /** * Renames the node * * @param string $name The new name * * @throws MethodNotAllowed not allowed to rename node */ public function setName($name) { throw new MethodNotAllowed(); } /** * Returns null, not supported * */ public function getLastModified() { return null; } /** * Delete tag to object association */ public function delete() { try { if (!$this->tagManager->canUserSeeTag($this->tag, $this->user)) { throw new NotFound('Tag with id ' . $this->tag->getId() . ' not found'); } if (!$this->tagManager->canUserAssignTag($this->tag, $this->user)) { throw new Forbidden('No permission to unassign tag ' . $this->tag->getId()); } $this->tagMapper->unassignTags($this->objectId, $this->objectType, $this->tag->getId()); } catch (TagNotFoundException $e) { // can happen if concurrent deletion occurred throw new NotFound('Tag with id ' . $this->tag->getId() . ' not found', 0, $e); } } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.21 |
proxy
|
phpinfo
|
Settings